feat: load N13 UIA dump fixture

This commit is contained in:
石头
2026-07-09 15:09:00 +08:00
parent d648c9476d
commit 4d5a9b0ae8
3 changed files with 129 additions and 1 deletions

View File

@@ -0,0 +1,109 @@
package uiaselector
import (
"bytes"
"encoding/json"
"fmt"
"os"
"strings"
)
type DumpResponse struct {
OK bool `json:"ok"`
Data *DumpData `json:"data"`
Error any `json:"error"`
}
type DumpData struct {
Root *Node `json:"root"`
}
type Node struct {
Name string `json:"name"`
ControlType string `json:"control_type"`
AutomationID string `json:"automation_id"`
ClassName string `json:"class_name"`
FrameworkID string `json:"framework_id"`
IsEnabled *bool `json:"is_enabled"`
IsOffscreen *bool `json:"is_offscreen"`
Bounds any `json:"bounds"`
Children []Node `json:"children"`
}
func LoadDumpFile(path string) (*Node, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
content = normalizeDumpJSON(content)
var response DumpResponse
if err := json.Unmarshal(content, &response); err != nil {
return nil, err
}
if !response.OK {
return nil, fmt.Errorf("dump response ok=false: %v", response.Error)
}
if response.Data == nil || response.Data.Root == nil {
return nil, fmt.Errorf("dump response has no data.root")
}
return response.Data.Root, nil
}
func CountNodes(root *Node) int {
if root == nil {
return 0
}
count := 1
for i := range root.Children {
count += CountNodes(&root.Children[i])
}
return count
}
func normalizeDumpJSON(content []byte) []byte {
content = bytes.TrimPrefix(content, []byte{0xEF, 0xBB, 0xBF})
source := string(content)
var out strings.Builder
out.Grow(len(source))
inString := false
escaped := false
for i := 0; i < len(source); {
ch := source[i]
if inString {
out.WriteByte(ch)
if escaped {
escaped = false
} else if ch == '\\' {
escaped = true
} else if ch == '"' {
inString = false
}
i++
continue
}
if ch == '"' {
inString = true
out.WriteByte(ch)
i++
continue
}
switch {
case strings.HasPrefix(source[i:], "-Infinity"):
out.WriteString("null")
i += len("-Infinity")
case strings.HasPrefix(source[i:], "Infinity"):
out.WriteString("null")
i += len("Infinity")
case strings.HasPrefix(source[i:], "NaN"):
out.WriteString("null")
i += len("NaN")
default:
out.WriteByte(ch)
i++
}
}
return []byte(out.String())
}

View File

@@ -0,0 +1,19 @@
package uiaselector
import "testing"
func TestLoadDumpFixtureParsesAcceptedN12RTree(t *testing.T) {
root, err := LoadDumpFile("testdata/n12r-2026-07-09-uia-redacted.json")
if err != nil {
t.Fatalf("LoadDumpFile: %v", err)
}
if root.AutomationID != "frmMain" {
t.Fatalf("root automation_id = %q, want frmMain", root.AutomationID)
}
if root.ControlType != "Window" {
t.Fatalf("root control_type = %q, want Window", root.ControlType)
}
if got := CountNodes(root); got != 66 {
t.Fatalf("CountNodes = %d, want 66", got)
}
}

File diff suppressed because one or more lines are too long