mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 06:42:46 +08:00
243 lines
7.0 KiB
Go
243 lines
7.0 KiB
Go
package uixt
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/httprunner/httprunner/v5/internal/builtin"
|
|
"github.com/httprunner/httprunner/v5/internal/config"
|
|
"github.com/httprunner/httprunner/v5/uixt/ai"
|
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
)
|
|
|
|
func preHandler_TapAbsXY(driver IDriver, options *option.ActionOptions, rawX, rawY float64) (
|
|
x, y float64, err error,
|
|
) {
|
|
// Call MCP action tool if anti-risk is enabled
|
|
if options.AntiRisk {
|
|
arguments := getAntiRisk_SetTouchInfoList_Arguments(driver, []ai.PointF{
|
|
{X: rawX, Y: rawY},
|
|
})
|
|
if arguments != nil {
|
|
callMCPActionTool(driver, "evalpkgs",
|
|
string(option.ACTION_SetTouchInfoList), arguments)
|
|
}
|
|
}
|
|
|
|
x, y = options.ApplyTapOffset(rawX, rawY)
|
|
|
|
// mark UI operation
|
|
if options.PreMarkOperation {
|
|
if markErr := MarkUIOperation(driver, option.ACTION_TapAbsXY, []float64{x, y}); markErr != nil {
|
|
log.Warn().Err(markErr).Msg("Failed to mark tap operation")
|
|
}
|
|
}
|
|
|
|
return x, y, nil
|
|
}
|
|
|
|
func preHandler_DoubleTap(driver IDriver, options *option.ActionOptions, rawX, rawY float64) (
|
|
x, y float64, err error,
|
|
) {
|
|
x, y, err = convertToAbsolutePoint(driver, rawX, rawY)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
x, y = options.ApplyTapOffset(x, y)
|
|
|
|
// mark UI operation
|
|
if options.PreMarkOperation {
|
|
if markErr := MarkUIOperation(driver, option.ACTION_DoubleTapXY, []float64{x, y}); markErr != nil {
|
|
log.Warn().Err(markErr).Msg("Failed to mark double tap operation")
|
|
}
|
|
}
|
|
|
|
return x, y, nil
|
|
}
|
|
|
|
func preHandler_Drag(driver IDriver, options *option.ActionOptions, rawFomX, rawFromY, rawToX, rawToY float64) (
|
|
fromX, fromY, toX, toY float64, err error,
|
|
) {
|
|
fromX, fromY, toX, toY, err = convertToAbsoluteCoordinates(driver, rawFomX, rawFromY, rawToX, rawToY)
|
|
if err != nil {
|
|
return 0, 0, 0, 0, err
|
|
}
|
|
fromX, fromY, toX, toY = options.ApplySwipeOffset(fromX, fromY, toX, toY)
|
|
|
|
// Call MCP action tool if anti-risk is enabled
|
|
if options.AntiRisk {
|
|
arguments := getAntiRisk_SetTouchInfoList_Arguments(driver, []ai.PointF{
|
|
{X: fromX, Y: fromY},
|
|
{X: toX, Y: toY},
|
|
})
|
|
if arguments != nil {
|
|
callMCPActionTool(driver, "evalpkgs",
|
|
string(option.ACTION_SetTouchInfoList), arguments)
|
|
}
|
|
}
|
|
|
|
// mark UI operation
|
|
if options.PreMarkOperation {
|
|
if markErr := MarkUIOperation(driver, option.ACTION_Drag, []float64{fromX, fromY, toX, toY}); markErr != nil {
|
|
log.Warn().Err(markErr).Msg("Failed to mark drag operation")
|
|
}
|
|
}
|
|
|
|
return fromX, fromY, toX, toY, nil
|
|
}
|
|
|
|
func preHandler_Swipe(driver IDriver, actionType option.ActionName,
|
|
options *option.ActionOptions, rawFomX, rawFromY, rawToX, rawToY float64) (
|
|
fromX, fromY, toX, toY float64, err error,
|
|
) {
|
|
fromX, fromY, toX, toY, err = convertToAbsoluteCoordinates(driver, rawFomX, rawFromY, rawToX, rawToY)
|
|
if err != nil {
|
|
return 0, 0, 0, 0, err
|
|
}
|
|
fromX, fromY, toX, toY = options.ApplySwipeOffset(fromX, fromY, toX, toY)
|
|
|
|
// Call MCP action tool if anti-risk is enabled
|
|
if options.AntiRisk {
|
|
arguments := getAntiRisk_SetTouchInfoList_Arguments(driver, []ai.PointF{
|
|
{X: fromX, Y: fromY},
|
|
{X: toX, Y: toY},
|
|
})
|
|
if arguments != nil {
|
|
callMCPActionTool(driver, "evalpkgs",
|
|
string(option.ACTION_SetTouchInfoList), arguments)
|
|
}
|
|
}
|
|
|
|
// save screenshot before action and mark UI operation
|
|
if options.PreMarkOperation {
|
|
if markErr := MarkUIOperation(driver, actionType, []float64{fromX, fromY, toX, toY}); markErr != nil {
|
|
log.Warn().Err(markErr).Msg("Failed to mark swipe operation")
|
|
}
|
|
}
|
|
|
|
return fromX, fromY, toX, toY, nil
|
|
}
|
|
|
|
func postHandler(driver IDriver, actionType option.ActionName, options *option.ActionOptions) error {
|
|
if options.AntiRisk && actionType == option.ACTION_SetTouchInfo {
|
|
arguments := getAntiRisk_SetTouchInfo_Arguments(driver)
|
|
if arguments != nil {
|
|
callMCPActionTool(driver, "evalpkgs", string(actionType), arguments)
|
|
}
|
|
}
|
|
|
|
// save screenshot after action
|
|
if options.PostMarkOperation {
|
|
// get compressed screenshot buffer
|
|
compressBufSource, err := getScreenShotBuffer(driver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// save compressed screenshot to file
|
|
timestamp := builtin.GenNameWithTimestamp("%d")
|
|
imagePath := filepath.Join(
|
|
config.GetConfig().ScreenShotsPath(),
|
|
fmt.Sprintf("%s_post_mark_%s.png", timestamp, actionType),
|
|
)
|
|
|
|
go func() {
|
|
err := saveScreenShot(compressBufSource, imagePath)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("save screenshot file failed")
|
|
}
|
|
}()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// callMCPActionTool calls MCP tool for the given action
|
|
func callMCPActionTool(driver IDriver,
|
|
serverName, actionType string, arguments map[string]any,
|
|
) {
|
|
// Get XTDriver from cache
|
|
dExt := getXTDriverFromCache(driver)
|
|
if dExt == nil {
|
|
log.Warn().Msg("XTDriver not found in cache, skipping MCP tool call")
|
|
return
|
|
}
|
|
|
|
// Create a context with timeout that can be cancelled
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
log.Debug().Str("server", serverName).Str("action", actionType).
|
|
Interface("arguments", arguments).Msg("calling MCP action tool")
|
|
|
|
// Call MCP tool with timeout context
|
|
result, err := dExt.CallMCPTool(ctx, serverName, actionType, arguments)
|
|
if err != nil {
|
|
// Classify error types for better debugging
|
|
if ctx.Err() == context.DeadlineExceeded {
|
|
log.Warn().Str("server", serverName).Str("action", actionType).
|
|
Msg("MCP action tool call timeout")
|
|
} else {
|
|
log.Warn().Err(err).Str("server", serverName).Str("action", actionType).
|
|
Msg("MCP action tool call failed")
|
|
}
|
|
return
|
|
}
|
|
|
|
log.Debug().Str("server", serverName).Str("action", actionType).
|
|
Interface("result", result).Msg("MCP action tool call succeeded")
|
|
}
|
|
|
|
// getAntiRisk_SetTouchInfo_Arguments gets arguments for SetTouchInfo MCP tool
|
|
func getAntiRisk_SetTouchInfo_Arguments(driver IDriver) map[string]interface{} {
|
|
arguments := getCommonMCPArguments(driver)
|
|
return arguments
|
|
}
|
|
|
|
// getAntiRisk_SetTouchInfoList_Arguments gets arguments for SetTouchInfoList MCP tool
|
|
func getAntiRisk_SetTouchInfoList_Arguments(driver IDriver, points []ai.PointF) map[string]interface{} {
|
|
arguments := getCommonMCPArguments(driver)
|
|
|
|
pointsList := make([]map[string]float64, len(points))
|
|
for i, point := range points {
|
|
pointsList[i] = map[string]float64{
|
|
"x": point.X,
|
|
"y": point.Y,
|
|
}
|
|
}
|
|
|
|
arguments["points"] = pointsList
|
|
arguments["clean"] = true
|
|
|
|
return arguments
|
|
}
|
|
|
|
// getCommonMCPArguments gets common arguments for MCP tools
|
|
func getCommonMCPArguments(driver IDriver) map[string]interface{} {
|
|
arguments := make(map[string]interface{})
|
|
|
|
device := driver.GetDevice()
|
|
|
|
// Get device model for Android devices
|
|
if adbDevice, ok := device.(*AndroidDevice); ok {
|
|
// Get device model
|
|
if deviceModel, err := adbDevice.Device.Model(); err == nil {
|
|
arguments["deviceModel"] = deviceModel
|
|
}
|
|
|
|
// Get device serial number
|
|
arguments["deviceSerial"] = adbDevice.Device.Serial()
|
|
}
|
|
|
|
// Get current foreground app info
|
|
if appInfo, err := driver.ForegroundInfo(); err == nil {
|
|
arguments["packageName"] = appInfo.PackageName
|
|
}
|
|
|
|
return arguments
|
|
}
|