-- Run: lua test/settings_calibration.lua -- Drives the settings app on a desktop Lua, through the shared fake device. Controls are -- pressed by label rather than by coordinate: where a row lands is layout, and layout is -- asserted in test/ui_layout_test.cpp against the C++ the panel actually runs. package.path = "sdcard/.lua/lib/?.lua;test/?.lua;" .. package.path local device = require("fake_device").install() local SETTINGS = "sdcard/.lua/apps/Settings/main.lua" local app = device.start(SETTINGS) local tapRow = device.tap -- Relaunching is how a test gets a clean screen, because that is what the firmware does: -- a new app is a new state that builds itself from nothing. local function restart() app = device.start(SETTINGS) end -- A perfectly linear panel spanning raw 200..3800 over 320x480 must round-trip -- to those same extremes from the two inset samples. local inset, w, h = 30, 320, 480 local function raw(pixel, size) return 200 + (3800 - 200) * pixel / size end local s1 = { x = raw(inset, w), y = raw(inset, h) } local s2 = { x = raw(w - inset, w), y = raw(h - inset, h) } local x0, y0, x1, y1 = app.computeCalibration(s1, s2, w, h, inset) assert(math.abs(x0 - 200) <= 1, "x0 " .. x0) assert(math.abs(y0 - 200) <= 1, "y0 " .. y0) assert(math.abs(x1 - 3800) <= 1, "x1 " .. x1) assert(math.abs(y1 - 3800) <= 1, "y1 " .. y1) -- A flipped panel (raw decreasing with pixel) must yield a descending range. local s3 = { x = 3800 - raw(inset, w) + 200, y = s1.y } local s4 = { x = 3800 - raw(w - inset, w) + 200, y = s2.y } local fx0, _, fx1 = app.computeCalibration(s3, s4, w, h, inset) assert(fx0 > fx1, "flipped axis should descend") -- Timezone cycles through the picker list. local zones = require "timezones" restart() tapRow "Timezone" assert(sys.getTimezone() == zones[2].tz, "timezone " .. sys.getTimezone()) -- Cycling rebuilds the screen, which is the only way a screen changes now, so the card -- shows the new zone and the menu is still the same size. local beforeCycle = tree.getCount() assert(device.labelled(zones[2].name), "timezone card did not take its new value") assert(tree.getCount() == beforeCycle, "the rebuilt menu grew") tapRow "Timezone" assert(sys.getTimezone() == zones[3].tz, "timezone " .. sys.getTimezone()) -- Rotation cycles through the four quarter turns and wraps back to 0. restart() for _, expected in ipairs { 90, 180, 270, 0 } do tapRow "Rotation" assert(screen.getRotation() == expected, "rotation " .. screen.getRotation()) end -- Calibration collects one sample per target and saves on the second release. restart() tapRow "Calibrate" app.tick() -- release after the menu tap arms sampling device.raw = { s1.x, s1.y } app.tick() device.raw = nil app.tick() device.raw = { s2.x, s2.y } app.tick() device.raw = nil app.tick() local saved = device.calibration assert(saved, "calibration was not saved") assert(math.abs(saved[1] - 200) <= 1, "saved x0 " .. saved[1]) -- WiFi controls reflect connection state without exposing configured network details on the card. device.status = { state = "disconnected", ssid = "", ip = "", rssi = 0 } restart() tapRow "WiFi" assert(not device.find "connect" and not device.find "disconnect", "unconfigured wifi has no toggle") device.status = { state = "disconnected", ssid = "saved", ip = "", rssi = 0 } restart() tapRow "WiFi" assert(device.find "connect" and not device.find "disconnect", "configured wifi can reconnect") tapRow "connect" assert(device.wifiReconnect, "wifi reconnect should use saved credentials") device.status = { state = "connected", ssid = "saved", ip = "192.168.1.2", rssi = -40 } restart() tapRow "WiFi" assert(device.find "disconnect" and not device.find "connect", "connected wifi can disconnect") tapRow "disconnect" assert(device.status.state == "disconnected" and device.status.ssid == "saved", "disconnect preserves wifi intent") -- Open networks connect directly from scan results. restart() device.networks = { { ssid = "qemu", rssi = -25, secure = false } } tapRow "WiFi" tapRow "scan networks" app.tick() tapRow "qemu" assert(device.connected, "open network should connect without a keyboard") assert(device.connected[1] == "qemu" and device.connected[2] == "", "open wifi connect") -- Secure networks route through the keyboard and preserve typed punctuation. restart() device.networks = { { ssid = "secure", rssi = -40, secure = true } } tapRow "WiFi" tapRow "scan networks" app.tick() tapRow "secure" app.tick() -- The keyboard is one custom node with no child per key, so its keys are reached through -- its own geometry rather than by label. getRect() answers the same box to the test and -- to the widget, which is what makes the two agree on where "q" is. local keyboard = require "keyboard" local board = device.findKind "custom" assert(board, "the secure network did not open a keyboard") local boardRect = { x = 0, y = 0, w = 10000, h = 10000 } local function typeKey(label) local pressed keyboard.eachKey("lower", boardRect, function(_, keyLabel, x, y, width) if keyLabel == label and not pressed then pressed = { x = x + width // 2, y = y + 15 } end end) assert(pressed, "no key labelled " .. label) device.press(board, pressed.x, pressed.y) end for key in ("qemuqemu"):gmatch "." do typeKey(key) end typeKey "OK" assert(device.connected[1] == "secure" and device.connected[2] == "qemuqemu", "secure wifi password") print "ok"