Extend autotest to cover all UART commands except OTA upload.
Add led_ring, find_me, restart, and ota_progress steps plus uart_cmds scenario. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a9e08107b4
commit
95d5a9747a
@ -43,7 +43,9 @@ Bench **configs** (`testdata/configs/`) list network, MACs, and serial ports (`u
|
||||
|
||||
```bash
|
||||
go run . test -list-configs
|
||||
go run . test -list-scenarios
|
||||
go run . test -config example-lab -scenario smoke
|
||||
go run . test -config example-lab -scenario uart_cmds
|
||||
go run . test -config my-lab -scenario smoke -port /dev/ttyUSB1 -v
|
||||
```
|
||||
|
||||
|
||||
@ -15,6 +15,10 @@ type MasterClient interface {
|
||||
ListClients() ([]*pb.ClientInfo, error)
|
||||
AccelDeadzone(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error)
|
||||
EspnowUnicastTest(clientID, seq uint32) (*pb.EspNowUnicastTestResponse, error)
|
||||
LedRing(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error)
|
||||
FindMe(clientID uint32) (*pb.EspNowFindMeResponse, error)
|
||||
Restart(clientID uint32) (*pb.RestartResponse, error)
|
||||
OtaSlaveProgress(clientID uint32) (*pb.OtaSlaveProgressResponse, error)
|
||||
}
|
||||
|
||||
type StepResult struct {
|
||||
@ -92,6 +96,14 @@ func runStep(bench *Bench, step Step, client MasterClient) error {
|
||||
return checkDeadzone(bench, step, client)
|
||||
case "unicast_test", "unicast":
|
||||
return checkUnicastTest(bench, step, client)
|
||||
case "led_ring", "ledring":
|
||||
return checkLedRing(step, client)
|
||||
case "find_me", "findme":
|
||||
return checkFindMe(bench, step, client)
|
||||
case "restart":
|
||||
return checkRestartCmd(bench, step, client)
|
||||
case "ota_progress", "ota_slave_progress":
|
||||
return checkOtaProgress(step, client)
|
||||
case "reset", "reboot":
|
||||
return checkReset(bench, step)
|
||||
default:
|
||||
@ -310,3 +322,203 @@ func checkUnicastTest(bench *Bench, step Step, client MasterClient) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ledRingInput struct {
|
||||
Mode string `json:"mode"`
|
||||
Progress uint `json:"progress"`
|
||||
Digit uint `json:"digit"`
|
||||
R uint `json:"r"`
|
||||
G uint `json:"g"`
|
||||
B uint `json:"b"`
|
||||
Intensity uint `json:"intensity"`
|
||||
BlinkMs uint `json:"blink_ms"`
|
||||
BlinkCount uint `json:"blink_count"`
|
||||
}
|
||||
|
||||
func ledRingModeValue(mode string) (uint32, error) {
|
||||
switch strings.ToLower(strings.ReplaceAll(mode, "-", "_")) {
|
||||
case "clear", "":
|
||||
return 0, nil
|
||||
case "progress":
|
||||
return 1, nil
|
||||
case "digit":
|
||||
return 2, nil
|
||||
case "blink":
|
||||
return 3, nil
|
||||
case "find_me", "findme":
|
||||
return 4, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown led_ring mode %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func checkLedRing(step Step, client MasterClient) error {
|
||||
var in ledRingInput
|
||||
if len(step.Input) > 0 {
|
||||
if err := json.Unmarshal(step.Input, &in); err != nil {
|
||||
return fmt.Errorf("input: %w", err)
|
||||
}
|
||||
}
|
||||
mode, err := ledRingModeValue(in.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if in.Mode == "" && step.Expect.Mode != nil {
|
||||
mode = *step.Expect.Mode
|
||||
}
|
||||
|
||||
req := &pb.LedRingProgressRequest{
|
||||
Mode: mode,
|
||||
Progress: uint32(in.Progress),
|
||||
Digit: uint32(in.Digit),
|
||||
R: uint32(in.R),
|
||||
G: uint32(in.G),
|
||||
B: uint32(in.B),
|
||||
Intensity: uint32(in.Intensity),
|
||||
BlinkMs: uint32(in.BlinkMs),
|
||||
BlinkCount: uint32(in.BlinkCount),
|
||||
}
|
||||
if mode == 1 && req.GetG() == 0 && req.GetR() == 0 && req.GetB() == 0 {
|
||||
req.G = 255
|
||||
}
|
||||
|
||||
resp, err := client.LedRing(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e := step.Expect
|
||||
if e.Success != nil && resp.GetSuccess() != *e.Success {
|
||||
return fmt.Errorf("success=%v want %v", resp.GetSuccess(), *e.Success)
|
||||
}
|
||||
if e.Mode != nil && resp.GetMode() != *e.Mode {
|
||||
return fmt.Errorf("mode=%d want %d", resp.GetMode(), *e.Mode)
|
||||
}
|
||||
if e.Progress != nil && resp.GetProgress() != *e.Progress {
|
||||
return fmt.Errorf("progress=%d want %d", resp.GetProgress(), *e.Progress)
|
||||
}
|
||||
if e.Digit != nil && resp.GetDigit() != *e.Digit {
|
||||
return fmt.Errorf("digit=%d want %d", resp.GetDigit(), *e.Digit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type findMeInput struct {
|
||||
ClientID *uint `json:"client_id"`
|
||||
Client uint `json:"client"`
|
||||
Slave string `json:"slave"`
|
||||
}
|
||||
|
||||
func checkFindMe(bench *Bench, step Step, client MasterClient) error {
|
||||
var in findMeInput
|
||||
if len(step.Input) > 0 {
|
||||
if err := json.Unmarshal(step.Input, &in); err != nil {
|
||||
return fmt.Errorf("input: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
clientID, err := resolveClientID(bench, in.Slave, in.ClientID, in.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.FindMe(clientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e := step.Expect
|
||||
if e.Success != nil && resp.GetSuccess() != *e.Success {
|
||||
return fmt.Errorf("success=%v want %v", resp.GetSuccess(), *e.Success)
|
||||
}
|
||||
if e.Slave != "" || in.Slave != "" {
|
||||
wantSlave := e.Slave
|
||||
if wantSlave == "" {
|
||||
wantSlave = in.Slave
|
||||
}
|
||||
wantID, err := bench.ResolveClientID(wantSlave)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.GetClientId() != wantID {
|
||||
return fmt.Errorf("client_id=%d want %d", resp.GetClientId(), wantID)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type restartCmdInput struct {
|
||||
ClientID *uint `json:"client_id"`
|
||||
Client uint `json:"client"`
|
||||
Slave string `json:"slave"`
|
||||
}
|
||||
|
||||
func checkRestartCmd(bench *Bench, step Step, client MasterClient) error {
|
||||
var in restartCmdInput
|
||||
if len(step.Input) > 0 {
|
||||
if err := json.Unmarshal(step.Input, &in); err != nil {
|
||||
return fmt.Errorf("input: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
clientID, err := resolveClientID(bench, in.Slave, in.ClientID, in.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := client.Restart(clientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e := step.Expect
|
||||
if e.Success != nil && resp.GetSuccess() != *e.Success {
|
||||
return fmt.Errorf("success=%v want %v", resp.GetSuccess(), *e.Success)
|
||||
}
|
||||
if resp.GetClientId() != clientID {
|
||||
return fmt.Errorf("client_id=%d want %d", resp.GetClientId(), clientID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type otaProgressInput struct {
|
||||
ClientID *uint `json:"client_id"`
|
||||
Client uint `json:"client"`
|
||||
Slave string `json:"slave"`
|
||||
}
|
||||
|
||||
func checkOtaProgress(step Step, client MasterClient) error {
|
||||
var in otaProgressInput
|
||||
if len(step.Input) > 0 {
|
||||
if err := json.Unmarshal(step.Input, &in); err != nil {
|
||||
return fmt.Errorf("input: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
clientID := uint32(in.Client)
|
||||
if in.ClientID != nil {
|
||||
clientID = uint32(*in.ClientID)
|
||||
}
|
||||
|
||||
resp, err := client.OtaSlaveProgress(clientID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e := step.Expect
|
||||
if e.Active != nil && resp.GetActive() != *e.Active {
|
||||
return fmt.Errorf("active=%v want %v", resp.GetActive(), *e.Active)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveClientID(bench *Bench, slave string, clientID *uint, client uint) (uint32, error) {
|
||||
switch {
|
||||
case slave != "":
|
||||
return bench.ResolveClientID(slave)
|
||||
case clientID != nil:
|
||||
return uint32(*clientID), nil
|
||||
default:
|
||||
return uint32(client), nil
|
||||
}
|
||||
}
|
||||
|
||||
25
goTool/autotest/runner_test.go
Normal file
25
goTool/autotest/runner_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package autotest
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLedRingModeValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
mode string
|
||||
want uint32
|
||||
}{
|
||||
{"clear", 0},
|
||||
{"progress", 1},
|
||||
{"digit", 2},
|
||||
{"blink", 3},
|
||||
{"find-me", 4},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got, err := ledRingModeValue(tc.mode)
|
||||
if err != nil {
|
||||
t.Fatalf("mode %q: %v", tc.mode, err)
|
||||
}
|
||||
if got != tc.want {
|
||||
t.Fatalf("mode %q = %d want %d", tc.mode, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,14 @@ type Expect struct {
|
||||
|
||||
// unicast_test
|
||||
Seq *uint32 `json:"seq,omitempty"`
|
||||
|
||||
// led_ring
|
||||
Mode *uint32 `json:"mode,omitempty"`
|
||||
Progress *uint32 `json:"progress,omitempty"`
|
||||
Digit *uint32 `json:"digit,omitempty"`
|
||||
|
||||
// ota_slave_progress
|
||||
Active *bool `json:"active,omitempty"`
|
||||
}
|
||||
|
||||
func LoadScenario(path string) (*Scenario, error) {
|
||||
|
||||
@ -222,3 +222,19 @@ func (s *serialPort) AccelDeadzone(req *pb.AccelDeadzoneRequest) (*pb.AccelDeadz
|
||||
func (s *serialPort) EspnowUnicastTest(clientID, seq uint32) (*pb.EspNowUnicastTestResponse, error) {
|
||||
return s.espnowUnicastTest(clientID, seq)
|
||||
}
|
||||
|
||||
func (s *serialPort) LedRing(req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
|
||||
return s.ledRingProgress(req)
|
||||
}
|
||||
|
||||
func (s *serialPort) FindMe(clientID uint32) (*pb.EspNowFindMeResponse, error) {
|
||||
return s.espnowFindMe(clientID)
|
||||
}
|
||||
|
||||
func (s *serialPort) Restart(clientID uint32) (*pb.RestartResponse, error) {
|
||||
return s.restart(clientID)
|
||||
}
|
||||
|
||||
func (s *serialPort) OtaSlaveProgress(clientID uint32) (*pb.OtaSlaveProgressResponse, error) {
|
||||
return QueryOtaSlaveProgress(s, clientID)
|
||||
}
|
||||
|
||||
21
goTool/testdata/README.md
vendored
21
goTool/testdata/README.md
vendored
@ -44,6 +44,19 @@ Ordered steps: UART commands, delays, or esptool reset.
|
||||
**unicast_test** — `input`: `slave` or `client_id`, `seq`
|
||||
`expect`: `success`, `seq`
|
||||
|
||||
**led_ring** — `input`: `mode` (`clear`, `progress`, `digit`, `blink`, `find_me`), `progress`, `digit`, `r`/`g`/`b`, `intensity`, `blink_ms`, `blink_count`
|
||||
`expect`: `success`, `mode`, `progress`, `digit`
|
||||
|
||||
**find_me** — `input`: `client` / `client_id` or `slave` (`0` = master ring)
|
||||
`expect`: `success`
|
||||
|
||||
**restart** — `input`: `client` / `client_id` or `slave` (prefer slave in tests so UART stays up)
|
||||
`expect`: `success`
|
||||
|
||||
**ota_progress** — query `OTA_SLAVE_PROGRESS` (no firmware upload)
|
||||
`input`: optional `client_id` / `slave`
|
||||
`expect`: `active` (typically `false` when idle)
|
||||
|
||||
**reset** — esptool hard-reset via console port from bench config (no `expect`).
|
||||
`input`: `target` (`master`) or `slave` (`pod-1`), or `all: true`; optional `wait_ms` after each reset (default 2000).
|
||||
|
||||
@ -56,13 +69,19 @@ Example reset steps:
|
||||
|
||||
Requires `python -m esptool` or `esptool.py` on PATH (ESP-IDF).
|
||||
|
||||
The `smoke` scenario resets every node with a configured `console` port, waits **10 s** for boot and ESP-NOW join, then runs the UART checks.
|
||||
The `smoke` scenario resets every node with a configured `console` port, waits **10 s** for boot and ESP-NOW join, then runs a short UART smoke test.
|
||||
|
||||
The **`uart_cmds`** scenario covers all implemented UART commands **except OTA upload** (`OTA_START` / `OTA_PAYLOAD` / `OTA_END` / `OTA_START_ESPNOW`). It ends with a slave **restart** (master UART stays connected).
|
||||
|
||||
`CLIENT_INPUT` is not tested (not implemented in firmware).
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
cd goTool
|
||||
go run . test -config example-lab -scenario smoke
|
||||
go run . test -config example-lab -scenario uart_cmds
|
||||
go run . test -config my-lab -scenario smoke -port /dev/ttyUSB1
|
||||
go run . test -list-configs
|
||||
go run . test -list-scenarios
|
||||
```
|
||||
|
||||
107
goTool/testdata/scenarios/uart_cmds.json
vendored
Normal file
107
goTool/testdata/scenarios/uart_cmds.json
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
{
|
||||
"id": "uart_cmds",
|
||||
"description": "All implemented UART commands (no OTA upload). Resets bench, joins ESP-NOW, exercises each cmd; restarts slave at end.",
|
||||
"config": "example-lab",
|
||||
"steps": [
|
||||
{
|
||||
"name": "reset all nodes",
|
||||
"command": "reset",
|
||||
"input": { "all": true, "wait_ms": 2500 }
|
||||
},
|
||||
{
|
||||
"name": "boot and ESP-NOW join",
|
||||
"delay_ms": 10000
|
||||
},
|
||||
{
|
||||
"name": "VERSION",
|
||||
"command": "version",
|
||||
"expect": { "version_min": 1 }
|
||||
},
|
||||
{
|
||||
"name": "CLIENT_INFO",
|
||||
"command": "clients",
|
||||
"expect": {
|
||||
"min_clients": 1,
|
||||
"slave": "pod-1",
|
||||
"available": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ACCEL_DEADZONE read master",
|
||||
"command": "deadzone",
|
||||
"input": { "write": false, "client": 0 },
|
||||
"expect": { "success": true }
|
||||
},
|
||||
{
|
||||
"name": "ACCEL_DEADZONE write master",
|
||||
"command": "deadzone",
|
||||
"input": { "write": true, "client": 0, "value": 120 },
|
||||
"expect": { "success": true, "deadzone": 120 }
|
||||
},
|
||||
{
|
||||
"name": "ACCEL_DEADZONE read master after write",
|
||||
"command": "deadzone",
|
||||
"input": { "write": false, "client": 0 },
|
||||
"expect": { "success": true, "deadzone": 120 }
|
||||
},
|
||||
{
|
||||
"name": "LED_RING clear",
|
||||
"command": "led_ring",
|
||||
"input": { "mode": "clear" },
|
||||
"expect": { "success": true, "mode": 0 }
|
||||
},
|
||||
{
|
||||
"name": "LED_RING progress",
|
||||
"command": "led_ring",
|
||||
"input": { "mode": "progress", "progress": 40, "g": 200 },
|
||||
"expect": { "success": true, "mode": 1, "progress": 40 }
|
||||
},
|
||||
{
|
||||
"name": "LED_RING digit",
|
||||
"command": "led_ring",
|
||||
"input": { "mode": "digit", "digit": 3, "r": 255 },
|
||||
"expect": { "success": true, "mode": 2, "digit": 3 }
|
||||
},
|
||||
{
|
||||
"name": "LED_RING blink",
|
||||
"command": "led_ring",
|
||||
"input": { "mode": "blink", "r": 0, "g": 255, "b": 0, "blink_count": 1 },
|
||||
"expect": { "success": true, "mode": 3 }
|
||||
},
|
||||
{
|
||||
"name": "ESPNOW_UNICAST_TEST",
|
||||
"command": "unicast_test",
|
||||
"input": { "slave": "pod-1", "seq": 99 },
|
||||
"expect": { "success": true, "seq": 99 }
|
||||
},
|
||||
{
|
||||
"name": "FIND_ME master",
|
||||
"command": "find_me",
|
||||
"input": { "client": 0 },
|
||||
"expect": { "success": true }
|
||||
},
|
||||
{
|
||||
"name": "FIND_ME slave",
|
||||
"command": "find_me",
|
||||
"input": { "slave": "pod-1" },
|
||||
"expect": { "success": true }
|
||||
},
|
||||
{
|
||||
"name": "ACCEL_DEADZONE push to slave",
|
||||
"command": "deadzone",
|
||||
"input": { "write": true, "slave": "pod-1", "value": 110 },
|
||||
"expect": { "success": true }
|
||||
},
|
||||
{
|
||||
"name": "OTA_SLAVE_PROGRESS idle",
|
||||
"command": "ota_progress",
|
||||
"expect": { "active": false }
|
||||
},
|
||||
{
|
||||
"name": "RESTART slave (last)",
|
||||
"command": "restart",
|
||||
"input": { "slave": "pod-1" },
|
||||
"expect": { "success": true }
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user