299 lines
7.1 KiB
AutoHotkey
299 lines
7.1 KiB
AutoHotkey
; Mosis Designer Test Utilities
|
|
; AutoHotkey v2
|
|
|
|
#Include "..\config.ahk"
|
|
|
|
; Find the designer window and return its handle
|
|
FindDesignerWindow() {
|
|
return WinExist(WINDOW_TITLE)
|
|
}
|
|
|
|
; Wait for the designer window to appear
|
|
; Returns window handle or 0 if timeout
|
|
WaitForDesignerWindow(timeout := 0) {
|
|
if (timeout = 0)
|
|
timeout := STARTUP_TIMEOUT
|
|
|
|
startTime := A_TickCount
|
|
while (A_TickCount - startTime < timeout) {
|
|
hwnd := FindDesignerWindow()
|
|
if (hwnd) {
|
|
; Give window time to fully initialize
|
|
Sleep(500)
|
|
return hwnd
|
|
}
|
|
Sleep(100)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
; Get the client area position of the designer window
|
|
; Returns {x, y, w, h} or 0 if window not found
|
|
GetClientArea(hwnd := 0) {
|
|
if (hwnd = 0)
|
|
hwnd := FindDesignerWindow()
|
|
if (!hwnd)
|
|
return 0
|
|
|
|
; Get window position
|
|
WinGetPos(&winX, &winY, &winW, &winH, hwnd)
|
|
|
|
; Get client area - for GLFW windows, typically minimal border
|
|
; Client area starts after title bar
|
|
; We'll estimate based on typical Windows decorations
|
|
; Title bar is usually ~30-40 pixels, borders ~8 pixels each side
|
|
|
|
; For a more accurate approach, we could use DllCall to GetClientRect
|
|
; But for GLFW windows, the client area often matches the requested size
|
|
|
|
; Calculate client area assuming standard decorations
|
|
borderWidth := 8
|
|
titleHeight := 31
|
|
|
|
return {
|
|
x: winX + borderWidth,
|
|
y: winY + titleHeight,
|
|
w: PHONE_WIDTH,
|
|
h: PHONE_HEIGHT
|
|
}
|
|
}
|
|
|
|
; Convert phone coordinates to screen coordinates
|
|
PhoneToScreen(phoneX, phoneY, hwnd := 0) {
|
|
client := GetClientArea(hwnd)
|
|
if (!client)
|
|
return 0
|
|
|
|
return {
|
|
x: client.x + phoneX,
|
|
y: client.y + phoneY
|
|
}
|
|
}
|
|
|
|
; Click at phone coordinates
|
|
ClickPhone(phoneX, phoneY, hwnd := 0) {
|
|
if (hwnd = 0)
|
|
hwnd := FindDesignerWindow()
|
|
if (!hwnd) {
|
|
LogMessage("ERROR: Designer window not found")
|
|
return false
|
|
}
|
|
|
|
; Activate window first
|
|
WinActivate(hwnd)
|
|
Sleep(50)
|
|
|
|
screen := PhoneToScreen(phoneX, phoneY, hwnd)
|
|
if (!screen) {
|
|
LogMessage("ERROR: Could not convert coordinates")
|
|
return false
|
|
}
|
|
|
|
; Move and click
|
|
Click(screen.x, screen.y)
|
|
Sleep(CLICK_DELAY)
|
|
|
|
LogMessage("Clicked at phone(" . phoneX . "," . phoneY . ") -> screen(" . screen.x . "," . screen.y . ")")
|
|
return true
|
|
}
|
|
|
|
; Click on a named app icon
|
|
ClickApp(appName, hwnd := 0) {
|
|
if (!APP_POSITIONS.Has(appName)) {
|
|
LogMessage("ERROR: Unknown app: " . appName)
|
|
return false
|
|
}
|
|
|
|
pos := APP_POSITIONS[appName]
|
|
LogMessage("Clicking app: " . appName)
|
|
return ClickPhone(pos.x, pos.y, hwnd)
|
|
}
|
|
|
|
; Click on a dock item
|
|
ClickDock(appName, hwnd := 0) {
|
|
if (!DOCK_POSITIONS.Has(appName)) {
|
|
LogMessage("ERROR: Unknown dock item: " . appName)
|
|
return false
|
|
}
|
|
|
|
pos := DOCK_POSITIONS[appName]
|
|
LogMessage("Clicking dock: " . appName)
|
|
return ClickPhone(pos.x, pos.y, hwnd)
|
|
}
|
|
|
|
; Send keyboard input to the designer
|
|
SendToDesigner(keys, hwnd := 0) {
|
|
if (hwnd = 0)
|
|
hwnd := FindDesignerWindow()
|
|
if (!hwnd)
|
|
return false
|
|
|
|
WinActivate(hwnd)
|
|
Sleep(50)
|
|
Send(keys)
|
|
return true
|
|
}
|
|
|
|
; Read the log file and check for a pattern
|
|
; Returns the matching line or empty string
|
|
CheckLogFor(pattern, logFile := 0) {
|
|
if (logFile = 0)
|
|
logFile := LOG_FILE
|
|
|
|
if (!FileExist(logFile))
|
|
return ""
|
|
|
|
content := FileRead(logFile)
|
|
lines := StrSplit(content, "`n")
|
|
|
|
; Search from end (most recent)
|
|
loop lines.Length {
|
|
idx := lines.Length - A_Index + 1
|
|
line := lines[idx]
|
|
if (InStr(line, pattern))
|
|
return line
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
; Wait for a specific pattern to appear in the log
|
|
WaitForLog(pattern, timeout := 0, logFile := 0) {
|
|
if (timeout = 0)
|
|
timeout := NAVIGATION_TIMEOUT
|
|
if (logFile = 0)
|
|
logFile := LOG_FILE
|
|
|
|
startTime := A_TickCount
|
|
while (A_TickCount - startTime < timeout) {
|
|
result := CheckLogFor(pattern, logFile)
|
|
if (result)
|
|
return result
|
|
Sleep(100)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
; Wait for navigation to a specific screen
|
|
WaitForNavigation(screenName, timeout := 0) {
|
|
pattern := "Navigated to: " . screenName
|
|
return WaitForLog(pattern, timeout)
|
|
}
|
|
|
|
; Wait for back navigation
|
|
WaitForBack(timeout := 0) {
|
|
return WaitForLog("Back to:", timeout)
|
|
}
|
|
|
|
; Take a screenshot of the designer window
|
|
CaptureScreenshot(filename, hwnd := 0) {
|
|
if (hwnd = 0)
|
|
hwnd := FindDesignerWindow()
|
|
if (!hwnd)
|
|
return false
|
|
|
|
; Ensure screenshot directory exists
|
|
if (!DirExist(SCREENSHOT_DIR))
|
|
DirCreate(SCREENSHOT_DIR)
|
|
|
|
fullPath := SCREENSHOT_DIR . "\" . filename
|
|
|
|
; Use built-in screenshot capability
|
|
; Note: This captures the entire window including decorations
|
|
try {
|
|
; Activate and bring to front
|
|
WinActivate(hwnd)
|
|
Sleep(100)
|
|
|
|
; Get window position
|
|
WinGetPos(&x, &y, &w, &h, hwnd)
|
|
|
|
; Use GDI+ or Windows API for screenshot
|
|
; For simplicity, we'll use the Snipping approach
|
|
; In production, you'd use a proper GDI+ screenshot
|
|
|
|
LogMessage("Screenshot requested: " . fullPath . " (not implemented)")
|
|
return false
|
|
} catch as e {
|
|
LogMessage("Screenshot error: " . e.Message)
|
|
return false
|
|
}
|
|
}
|
|
|
|
; Log a message with timestamp
|
|
LogMessage(msg) {
|
|
timestamp := FormatTime(, "yyyy-MM-dd HH:mm:ss")
|
|
line := "[" . timestamp . "] " . msg . "`n"
|
|
|
|
; Write to console
|
|
OutputDebug(line)
|
|
|
|
; Also append to a test log file
|
|
testLog := A_ScriptDir . "\test_run.log"
|
|
FileAppend(line, testLog)
|
|
}
|
|
|
|
; Clear test logs
|
|
ClearLogs() {
|
|
testLog := A_ScriptDir . "\test_run.log"
|
|
if (FileExist(testLog))
|
|
FileDelete(testLog)
|
|
if (FileExist(LOG_FILE))
|
|
FileDelete(LOG_FILE)
|
|
}
|
|
|
|
; Close the designer window
|
|
CloseDesigner(hwnd := 0) {
|
|
if (hwnd = 0)
|
|
hwnd := FindDesignerWindow()
|
|
if (hwnd) {
|
|
WinClose(hwnd)
|
|
Sleep(500)
|
|
}
|
|
}
|
|
|
|
; Kill any running designer processes
|
|
KillDesigner() {
|
|
try {
|
|
Run('taskkill /F /IM mosis-designer.exe', , "Hide")
|
|
}
|
|
Sleep(500)
|
|
}
|
|
|
|
; Start the designer process with log redirection
|
|
; Returns the process ID or 0 on failure
|
|
StartDesigner(rmlFile := 0) {
|
|
if (rmlFile = 0)
|
|
rmlFile := HOME_RML
|
|
|
|
; Build command with output redirection
|
|
cmd := '"' . DESIGNER_EXE . '" "' . rmlFile . '"'
|
|
|
|
LogMessage("Starting designer: " . cmd)
|
|
|
|
; Start process and capture output
|
|
; We'll redirect to a log file
|
|
fullCmd := A_ComSpec . ' /c "' . cmd . '" > "' . LOG_FILE . '" 2>&1'
|
|
|
|
Run(fullCmd, A_ScriptDir, "Hide", &pid)
|
|
|
|
if (pid) {
|
|
LogMessage("Designer started with PID: " . pid)
|
|
return pid
|
|
}
|
|
|
|
LogMessage("ERROR: Failed to start designer")
|
|
return 0
|
|
}
|
|
|
|
; Assert helper - logs pass/fail and returns result
|
|
Assert(condition, testName) {
|
|
if (condition) {
|
|
LogMessage("PASS: " . testName)
|
|
return true
|
|
} else {
|
|
LogMessage("FAIL: " . testName)
|
|
return false
|
|
}
|
|
}
|