feat: uatu-sample-harness for SDK round-trip validation

Listens on TCP, runs adb reverse to expose the host port as the
device's localabstract:uatu-agent socket, then drives N PAUSE/STATE
/RESUME cycles against the connected SDK. Cleanly removes the adb
forward on exit.
This commit is contained in:
pj committed 2026-04-17 23:39:07 +07:00
1 parent 8961aadda8
commit d991d99e0f
1 file changed
+98
+98
View File
@@ -0,0 +1,98 @@
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"github.com/priyanshujain/uatu/internal/agent"
)
func main() {
port := flag.Int("port", 0, "TCP port to listen on (0 = ephemeral)")
socketName := flag.String("socket-name", "uatu-agent", "abstract socket name on the device")
cycles := flag.Int("cycles", 3, "number of snapshot cycles to run")
serial := flag.String("serial", "", "adb device serial (empty = default)")
cycleInterval := flag.Duration("interval", 500*time.Millisecond, "delay between snapshot cycles")
flag.Parse()
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
if err != nil {
die("listen: %v", err)
}
defer listener.Close()
listenerAddr := listener.Addr().(*net.TCPAddr)
fmt.Printf("listening on 127.0.0.1:%d\n", listenerAddr.Port)
if err := adbReverse(*serial, *socketName, listenerAddr.Port); err != nil {
die("adb reverse: %v", err)
}
defer func() {
if err := adbReverseRemove(*serial, *socketName); err != nil {
fmt.Fprintf(os.Stderr, "adb reverse cleanup: %v\n", err)
}
}()
fmt.Printf("forwarded localabstract:%s -> tcp:%d on device\n", *socketName, listenerAddr.Port)
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
server := agent.NewServer(listener)
fmt.Println("waiting for SDK to connect (launch the sample app on the device)...")
conn, err := server.Accept(ctx)
if err != nil {
die("accept: %v", err)
}
defer conn.Close()
hello := conn.Hello()
fmt.Printf("HELLO from platform=%s app=%s sdk=%s\n", hello.Platform, hello.AppPackage, hello.Version)
for index := 1; index <= *cycles; index++ {
snapshotCtx, snapshotCancel := context.WithTimeout(ctx, 5*time.Second)
state, err := conn.Snapshot(snapshotCtx)
snapshotCancel()
if err != nil {
die("snapshot #%d: %v", index, err)
}
marshalled, _ := json.Marshal(state.Snapshots)
fmt.Printf("STATE %d: %s\n", index, marshalled)
if err := conn.Release(ctx); err != nil {
die("release #%d: %v", index, err)
}
if index < *cycles {
time.Sleep(*cycleInterval)
}
}
fmt.Println("round-trip complete")
}
func adbReverse(serial, socketName string, port int) error {
commandArguments := []string{"reverse", fmt.Sprintf("localabstract:%s", socketName), fmt.Sprintf("tcp:%d", port)}
if serial != "" {
commandArguments = append([]string{"-s", serial}, commandArguments...)
}
command := exec.Command("adb", commandArguments...)
command.Stdout = os.Stdout
command.Stderr = os.Stderr
return command.Run()
}
func adbReverseRemove(serial, socketName string) error {
commandArguments := []string{"reverse", "--remove", fmt.Sprintf("localabstract:%s", socketName)}
if serial != "" {
commandArguments = append([]string{"-s", serial}, commandArguments...)
}
return exec.Command("adb", commandArguments...).Run()
}
func die(format string, args ...any) {
fmt.Fprintf(os.Stderr, "error: "+format+"\n", args...)
os.Exit(1)
}