Files
slate32/test/settings_calibration.lua
T
evan 61c2f9a6fb refactor(ui): adopt tree-owned scrolling and the ui.* module namespace
Scroll and optionlist drop their own drag/flick controllers now that
ui.lua owns them; main forwards deltaMs to ui.draw. keyboard, optionlist
and statusbar move under ui.* -- keyboard into the shared submodule,
optionlist/statusbar to sdcard/.lua/lib/ui. Bumps esp32-lua-api.
2026-08-06 17:08:07 -04:00

143 lines
5.9 KiB
Lua

-- Run: lua test/settings_calibration.lua
-- Drives the settings apps on a desktop Lua, through the shared fake device. Settings is a
-- menu of sub-apps now: the menu launches routes, and each setting is its own app started
-- on its own. Controls are pressed by label, because where a row lands is layout and layout
-- is asserted in test/ui_layout_test.cpp against the C++ the panel runs.
package.path = "sdcard/.lua/lib/?.lua;lib/esp32-lua-api/lua/lib/?.lua;test/?.lua;" .. package.path
local device = require("fake_device").install()
local APP = "sdcard/.lua/apps/Settings/"
-- The menu launches one route per card, and nothing else: tapping a card only records the
-- navigation the firmware would perform.
device.start(APP .. "main.lua")
local routes = {
Calibrate = "Settings/Calibrate",
Rotation = "Settings/Rotation",
WiFi = "Settings/Wifi",
BLE = "Settings/Ble",
Theme = "Settings/Theme",
Timezone = "Settings/Timezone",
}
for label, route in pairs(routes) do
device.started = nil
device.tap(label)
assert(device.started, label .. " launched nothing")
assert(device.started.args.app == route, label .. " launched " .. tostring(device.started.args.app))
end
-- computeCalibration: a perfectly linear panel spanning raw 200..3800 over 320x480 must
-- round-trip to those extremes from the two inset samples.
local calibrate = device.start(APP .. "Calibrate/main.lua")
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 = calibrate.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 = calibrate.computeCalibration(s3, s4, w, h, inset)
assert(fx0 > fx1, "flipped axis should descend")
-- Calibration collects one sample per target and saves on the second release. Starting the
-- app is entering calibration, so the first tick arms sampling after the launch tap lifts.
calibrate = device.start(APP .. "Calibrate/main.lua")
calibrate.tick()
device.raw = { s1.x, s1.y }
calibrate.tick()
device.raw = nil
calibrate.tick()
device.raw = { s2.x, s2.y }
calibrate.tick()
device.raw = nil
calibrate.tick()
local saved = device.calibration
assert(saved, "calibration was not saved")
assert(math.abs(saved[1] - 200) <= 1, "saved x0 " .. saved[1])
-- Rotation is an option list: tapping a quarter turn applies it and marks the new current.
device.rotation = 0
local rotation = device.start(APP .. "Rotation/main.lua")
device.tap "90 degrees"
assert(screen.getRotation() == 90, "rotation " .. screen.getRotation())
rotation = device.start(APP .. "Rotation/main.lua")
device.tap "270 degrees"
assert(screen.getRotation() == 270, "rotation " .. screen.getRotation())
-- Timezone is the same list, keyed by the picker's zones.
local zones = require "timezones"
device.timezone = zones[1].tz
device.start(APP .. "Timezone/main.lua")
device.tap(zones[3].name)
assert(sys.getTimezone() == zones[3].tz, "timezone " .. sys.getTimezone())
-- WiFi controls reflect connection state without exposing configured network details.
device.status = { state = "disconnected", ssid = "", ip = "", rssi = 0 }
device.start(APP .. "Wifi/main.lua")
assert(not device.find "connect" and not device.find "disconnect", "unconfigured wifi has no toggle")
device.status = { state = "disconnected", ssid = "saved", ip = "", rssi = 0 }
device.start(APP .. "Wifi/main.lua")
assert(device.find "connect" and not device.find "disconnect", "configured wifi can reconnect")
device.tap "connect"
assert(device.wifiReconnect, "wifi reconnect should use saved credentials")
device.status = { state = "connected", ssid = "saved", ip = "192.168.1.2", rssi = -40 }
local wifiApp = device.start(APP .. "Wifi/main.lua")
assert(device.find "disconnect" and not device.find "connect", "connected wifi can disconnect")
device.tap "disconnect"
assert(device.status.state == "disconnected" and device.status.ssid == "saved", "disconnect preserves wifi intent")
-- Open networks connect directly from scan results.
wifiApp = device.start(APP .. "Wifi/main.lua")
device.networks = { { ssid = "qemu", rssi = -25, secure = false } }
device.tap "scan networks"
wifiApp.tick()
device.tap "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.
wifiApp = device.start(APP .. "Wifi/main.lua")
device.networks = { { ssid = "secure", rssi = -40, secure = true } }
device.tap "scan networks"
wifiApp.tick()
device.tap "secure"
wifiApp.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 "ui.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"