mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 08:12:41 +08:00
add simulation
This commit is contained in:
@@ -284,7 +284,7 @@ func TestSwipeWithDirection(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := driver.SwipeWithDirection(
|
||||
err := driver.SIMSwipeWithDirection(
|
||||
tc.direction,
|
||||
tc.startX,
|
||||
tc.startY,
|
||||
@@ -316,19 +316,19 @@ func TestSwipeWithDirectionInvalidInputs(t *testing.T) {
|
||||
defer driver.TearDown()
|
||||
|
||||
// Test invalid direction
|
||||
err = driver.SwipeWithDirection("invalid", 500.0, 500.0, 100.0, 200.0)
|
||||
err = driver.SIMSwipeWithDirection("invalid", 500.0, 500.0, 100.0, 200.0)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid direction, but got none")
|
||||
}
|
||||
|
||||
// Test invalid distance range (max < min)
|
||||
err = driver.SwipeWithDirection("up", 500.0, 500.0, 200.0, 100.0)
|
||||
err = driver.SIMSwipeWithDirection("up", 500.0, 500.0, 200.0, 100.0)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid distance range, but got none")
|
||||
}
|
||||
|
||||
// Test zero distance
|
||||
err = driver.SwipeWithDirection("up", 500.0, 500.0, 0.0, 0.0)
|
||||
err = driver.SIMSwipeWithDirection("up", 500.0, 500.0, 0.0, 0.0)
|
||||
if err == nil {
|
||||
t.Error("Expected error for zero distance, but got none")
|
||||
}
|
||||
@@ -376,7 +376,7 @@ func TestSwipeInArea(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
for i := 0; i < 3; i++ {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := driver.SwipeInArea(
|
||||
err := driver.SIMSwipeInArea(
|
||||
tc.direction,
|
||||
tc.areaStartX,
|
||||
tc.areaStartY,
|
||||
@@ -429,7 +429,7 @@ func TestSwipeFromPointToPoint(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := driver.SwipeFromPointToPoint(
|
||||
err := driver.SIMSwipeFromPointToPoint(
|
||||
tc.startX,
|
||||
tc.startY,
|
||||
tc.endX,
|
||||
@@ -460,13 +460,13 @@ func TestSwipeFromPointToPointInvalidInputs(t *testing.T) {
|
||||
defer driver.TearDown()
|
||||
|
||||
// Test same start and end point
|
||||
err = driver.SwipeFromPointToPoint(0.5, 0.5, 0.5, 0.5)
|
||||
err = driver.SIMSwipeFromPointToPoint(0.5, 0.5, 0.5, 0.5)
|
||||
if err == nil {
|
||||
t.Error("Expected error for same start and end point, but got none")
|
||||
}
|
||||
|
||||
// Test very close points (should result in distance too short)
|
||||
err = driver.SwipeFromPointToPoint(0.5, 0.5, 0.501, 0.501)
|
||||
err = driver.SIMSwipeFromPointToPoint(0.5, 0.5, 0.501, 0.501)
|
||||
if err == nil {
|
||||
t.Error("Expected error for very close points, but got none")
|
||||
}
|
||||
@@ -503,7 +503,7 @@ func TestClickAtPoint(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := driver.ClickAtPoint(tc.x, tc.y)
|
||||
err := driver.SIMClickAtPoint(tc.x, tc.y)
|
||||
if err != nil {
|
||||
t.Errorf("ClickAtPoint failed: %v", err)
|
||||
} else {
|
||||
@@ -529,21 +529,91 @@ func TestClickAtPointInvalidInputs(t *testing.T) {
|
||||
defer driver.TearDown()
|
||||
|
||||
// Test negative coordinates
|
||||
err = driver.ClickAtPoint(-0.1, 0.5)
|
||||
err = driver.SIMClickAtPoint(-0.1, 0.5)
|
||||
if err == nil {
|
||||
t.Error("Expected error for negative x coordinate, but got none")
|
||||
}
|
||||
|
||||
err = driver.ClickAtPoint(0.5, -0.1)
|
||||
err = driver.SIMClickAtPoint(0.5, -0.1)
|
||||
if err == nil {
|
||||
t.Error("Expected error for negative y coordinate, but got none")
|
||||
}
|
||||
|
||||
// Test coordinates out of range (though these should be handled by convertToAbsolutePoint)
|
||||
err = driver.ClickAtPoint(1.5, 0.5)
|
||||
err = driver.SIMClickAtPoint(1.5, 0.5)
|
||||
if err != nil {
|
||||
t.Logf("Out of range coordinates handled properly: %v", err)
|
||||
}
|
||||
|
||||
t.Log("Click invalid input validation tests passed")
|
||||
}
|
||||
|
||||
func TestSIMInput(t *testing.T) {
|
||||
device, err := uixt.NewAndroidDevice(
|
||||
option.WithSerialNumber(""),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
driver, err := uixt.NewUIA2Driver(device)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer driver.TearDown()
|
||||
|
||||
// Test cases for different text inputs
|
||||
testCases := []struct {
|
||||
name string
|
||||
text string
|
||||
}{
|
||||
//{
|
||||
// name: "英文短文本",
|
||||
// text: "Hello",
|
||||
//},
|
||||
//{
|
||||
// name: "英文长文本",
|
||||
// text: "Hello World! This is a test message.",
|
||||
//},
|
||||
//{
|
||||
// name: "日文文本",
|
||||
// text: "英語の長い文字",
|
||||
//},
|
||||
//{
|
||||
// name: "混合文本",
|
||||
// text: "Hello你好123",
|
||||
//},
|
||||
//{
|
||||
// name: "特殊字符",
|
||||
// text: "!@#$%^&*()",
|
||||
//},
|
||||
//{
|
||||
// name: "数字文本",
|
||||
// text: "1234567890",
|
||||
//},
|
||||
//{
|
||||
// name: "空文本",
|
||||
// text: "",
|
||||
//},
|
||||
//{
|
||||
// name: "单个字符",
|
||||
// text: "A",
|
||||
//},
|
||||
{
|
||||
name: "长文本",
|
||||
text: "This is a very long text to test the performance of SIMInput function. 这是一个很长的文本用来测试SIMInput函数的性能。1234567890!@#$%^&*()英語の長い文字",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := driver.SIMInput(tc.text)
|
||||
// err := driver.Input(tc.text)
|
||||
if err != nil {
|
||||
t.Errorf("SIMInput failed: %v", err)
|
||||
} else {
|
||||
t.Logf("Successfully executed SIMInput: %s with text '%s'", tc.name, tc.text)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
325
internal/simulation/input_api.go
Normal file
325
internal/simulation/input_api.go
Normal file
@@ -0,0 +1,325 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// InputRequest 输入请求参数
|
||||
type InputRequest struct {
|
||||
Text string `json:"text"` // 输入文本
|
||||
MinSegmentLen int `json:"min_segment"` // 最小分割长度
|
||||
MaxSegmentLen int `json:"max_segment"` // 最大分割长度
|
||||
MinDelayMs int `json:"min_delay_ms"` // 最小延迟时间(毫秒)
|
||||
MaxDelayMs int `json:"max_delay_ms"` // 最大延迟时间(毫秒)
|
||||
}
|
||||
|
||||
// InputResponse 输入响应结果
|
||||
type InputResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Segments []InputSegment `json:"segments"`
|
||||
Metrics InputMetrics `json:"metrics"`
|
||||
}
|
||||
|
||||
// InputSegment 输入片段
|
||||
type InputSegment struct {
|
||||
Index int `json:"index"` // 片段索引
|
||||
Text string `json:"text"` // 片段文本
|
||||
DelayMs int `json:"delay_ms"` // 该片段后的延迟时间(毫秒)
|
||||
CharLen int `json:"char_len"` // 字符长度
|
||||
}
|
||||
|
||||
// InputMetrics 输入指标
|
||||
type InputMetrics struct {
|
||||
TotalSegments int `json:"total_segments"` // 总片段数
|
||||
TotalDelayMs int `json:"total_delay_ms"` // 总延迟时间
|
||||
EstimatedTimeMs int `json:"estimated_time_ms"` // 预估总耗时
|
||||
OriginalCharLen int `json:"original_char_len"` // 原始字符长度
|
||||
}
|
||||
|
||||
// InputConfig 输入配置参数
|
||||
type InputConfig struct {
|
||||
MinSegmentLen int // 最小分割长度(字符数)
|
||||
MaxSegmentLen int // 最大分割长度(字符数)
|
||||
MinDelayMs int // 最小延迟时间(毫秒)
|
||||
MaxDelayMs int // 最大延迟时间(毫秒)
|
||||
}
|
||||
|
||||
// DefaultInputConfig 默认输入配置
|
||||
var DefaultInputConfig = InputConfig{
|
||||
MinSegmentLen: 1, // 1个字符
|
||||
MaxSegmentLen: 4, // 4个字符
|
||||
MinDelayMs: 50, // 50毫秒
|
||||
MaxDelayMs: 200, // 200毫秒
|
||||
}
|
||||
|
||||
// InputSimulatorAPI 输入仿真API
|
||||
type InputSimulatorAPI struct {
|
||||
rand *rand.Rand
|
||||
config InputConfig
|
||||
}
|
||||
|
||||
// NewInputSimulatorAPI 创建新的输入仿真API
|
||||
func NewInputSimulatorAPI(config *InputConfig) *InputSimulatorAPI {
|
||||
if config == nil {
|
||||
config = &DefaultInputConfig
|
||||
}
|
||||
|
||||
return &InputSimulatorAPI{
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
config: *config,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateInputSegments 生成输入片段序列
|
||||
func (api *InputSimulatorAPI) GenerateInputSegments(req InputRequest) InputResponse {
|
||||
// 验证输入参数
|
||||
if err := api.validateRequest(req); err != nil {
|
||||
return InputResponse{
|
||||
Success: false,
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// 如果文本为空,直接返回
|
||||
if req.Text == "" {
|
||||
return InputResponse{
|
||||
Success: true,
|
||||
Segments: []InputSegment{},
|
||||
Metrics: InputMetrics{
|
||||
TotalSegments: 0,
|
||||
TotalDelayMs: 0,
|
||||
EstimatedTimeMs: 0,
|
||||
OriginalCharLen: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 生成分割片段
|
||||
segments := api.splitTextIntelligently(req.Text, req.MinSegmentLen, req.MaxSegmentLen)
|
||||
|
||||
// 生成延迟时间
|
||||
inputSegments := make([]InputSegment, len(segments))
|
||||
totalDelayMs := 0
|
||||
|
||||
for i, segment := range segments {
|
||||
var delayMs int
|
||||
// 最后一个片段不需要延迟
|
||||
if i < len(segments)-1 {
|
||||
delayMs = api.generateRandomDelay(req.MinDelayMs, req.MaxDelayMs)
|
||||
totalDelayMs += delayMs
|
||||
}
|
||||
|
||||
inputSegments[i] = InputSegment{
|
||||
Index: i,
|
||||
Text: segment,
|
||||
DelayMs: delayMs,
|
||||
CharLen: len([]rune(segment)),
|
||||
}
|
||||
}
|
||||
|
||||
// 计算指标
|
||||
metrics := InputMetrics{
|
||||
TotalSegments: len(segments),
|
||||
TotalDelayMs: totalDelayMs,
|
||||
EstimatedTimeMs: totalDelayMs, // 简化计算,实际输入时间可能更长
|
||||
OriginalCharLen: len([]rune(req.Text)),
|
||||
}
|
||||
|
||||
return InputResponse{
|
||||
Success: true,
|
||||
Segments: inputSegments,
|
||||
Metrics: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
// validateRequest 验证请求参数
|
||||
func (api *InputSimulatorAPI) validateRequest(req InputRequest) error {
|
||||
// 使用配置中的默认值填充请求参数
|
||||
if req.MinSegmentLen <= 0 {
|
||||
req.MinSegmentLen = api.config.MinSegmentLen
|
||||
}
|
||||
if req.MaxSegmentLen <= 0 {
|
||||
req.MaxSegmentLen = api.config.MaxSegmentLen
|
||||
}
|
||||
if req.MinDelayMs < 0 {
|
||||
req.MinDelayMs = api.config.MinDelayMs
|
||||
}
|
||||
if req.MaxDelayMs < 0 {
|
||||
req.MaxDelayMs = api.config.MaxDelayMs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitTextIntelligently 智能分割文本
|
||||
// 规则:
|
||||
// 1. 先分解成基础单元:中文每个字符一个单元,英文/数字连续的作为一个单元,其他字符各自一个单元
|
||||
// 2. 按MinSegmentLen到MaxSegmentLen的随机值组合基础单元
|
||||
func (api *InputSimulatorAPI) splitTextIntelligently(text string, minLen, maxLen int) []string {
|
||||
if minLen <= 0 {
|
||||
minLen = api.config.MinSegmentLen
|
||||
}
|
||||
if maxLen <= 0 {
|
||||
maxLen = api.config.MaxSegmentLen
|
||||
}
|
||||
if maxLen < minLen {
|
||||
maxLen = minLen
|
||||
}
|
||||
|
||||
// 第一步:分解成基础单元
|
||||
baseUnits := api.splitIntoBaseUnits(text)
|
||||
|
||||
// 第二步:按随机数组合基础单元
|
||||
var segments []string
|
||||
i := 0
|
||||
|
||||
for i < len(baseUnits) {
|
||||
remainingUnits := len(baseUnits) - i
|
||||
|
||||
var unitCount int
|
||||
// 如果剩余单元数少于minLen,就把剩余的全部作为一个片段
|
||||
if remainingUnits < minLen {
|
||||
unitCount = remainingUnits
|
||||
} else {
|
||||
// 随机决定本次要组合的单元数量(在minLen到maxLen之间)
|
||||
unitCount = minLen
|
||||
if maxLen > minLen {
|
||||
// 确保unitCount不超过剩余单元数
|
||||
maxPossibleCount := maxLen
|
||||
if maxPossibleCount > remainingUnits {
|
||||
maxPossibleCount = remainingUnits
|
||||
}
|
||||
unitCount = minLen + api.rand.Intn(maxPossibleCount-minLen+1)
|
||||
}
|
||||
}
|
||||
|
||||
// 组合unitCount个基础单元成一个片段
|
||||
segment := ""
|
||||
for j := 0; j < unitCount; j++ {
|
||||
segment += baseUnits[i+j]
|
||||
}
|
||||
segments = append(segments, segment)
|
||||
i += unitCount
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
// splitIntoBaseUnits 将文本分解成基础单元
|
||||
func (api *InputSimulatorAPI) splitIntoBaseUnits(text string) []string {
|
||||
var units []string
|
||||
runes := []rune(text)
|
||||
i := 0
|
||||
|
||||
for i < len(runes) {
|
||||
// 处理中文字符:每个字符一个单元
|
||||
if api.isChinese(runes[i]) {
|
||||
units = append(units, string(runes[i]))
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理连续英文字母:作为一个单元
|
||||
if unicode.IsLetter(runes[i]) && runes[i] <= 127 {
|
||||
start := i
|
||||
for i < len(runes) && unicode.IsLetter(runes[i]) && runes[i] <= 127 {
|
||||
i++
|
||||
}
|
||||
word := string(runes[start:i])
|
||||
units = append(units, word)
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理连续数字:作为一个单元
|
||||
if unicode.IsDigit(runes[i]) {
|
||||
start := i
|
||||
for i < len(runes) && unicode.IsDigit(runes[i]) {
|
||||
i++
|
||||
}
|
||||
number := string(runes[start:i])
|
||||
units = append(units, number)
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理其他字符(空格、标点等):每个字符一个单元
|
||||
units = append(units, string(runes[i]))
|
||||
i++
|
||||
}
|
||||
|
||||
return units
|
||||
}
|
||||
|
||||
// isChinese 判断字符是否为中文
|
||||
func (api *InputSimulatorAPI) isChinese(r rune) bool {
|
||||
return unicode.Is(unicode.Scripts["Han"], r)
|
||||
}
|
||||
|
||||
// splitTextRandomly 将文本随机分割成指定长度范围的片段(保留原有方法作为备用)
|
||||
func (api *InputSimulatorAPI) splitTextRandomly(text string, minLen, maxLen int) []string {
|
||||
var segments []string
|
||||
runes := []rune(text) // 使用rune来正确处理多字节字符(如中文)
|
||||
|
||||
if minLen <= 0 {
|
||||
minLen = api.config.MinSegmentLen
|
||||
}
|
||||
if maxLen <= 0 {
|
||||
maxLen = api.config.MaxSegmentLen
|
||||
}
|
||||
if maxLen < minLen {
|
||||
maxLen = minLen
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(runes) {
|
||||
// 随机决定本次分割的长度
|
||||
segmentLength := minLen
|
||||
if maxLen > minLen {
|
||||
segmentLength = minLen + api.rand.Intn(maxLen-minLen+1)
|
||||
}
|
||||
|
||||
// 确保不超出文本长度
|
||||
if i+segmentLength > len(runes) {
|
||||
segmentLength = len(runes) - i
|
||||
}
|
||||
|
||||
// 提取片段
|
||||
segment := string(runes[i : i+segmentLength])
|
||||
segments = append(segments, segment)
|
||||
|
||||
i += segmentLength
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
// generateRandomDelay 生成随机延迟时间
|
||||
func (api *InputSimulatorAPI) generateRandomDelay(minDelayMs, maxDelayMs int) int {
|
||||
if minDelayMs < 0 {
|
||||
minDelayMs = api.config.MinDelayMs
|
||||
}
|
||||
if maxDelayMs < 0 {
|
||||
maxDelayMs = api.config.MaxDelayMs
|
||||
}
|
||||
if maxDelayMs < minDelayMs {
|
||||
maxDelayMs = minDelayMs
|
||||
}
|
||||
|
||||
if maxDelayMs == minDelayMs {
|
||||
return minDelayMs
|
||||
}
|
||||
|
||||
return minDelayMs + api.rand.Intn(maxDelayMs-minDelayMs+1)
|
||||
}
|
||||
|
||||
// SplitText 公开的文本分割函数(使用智能分割)
|
||||
func (api *InputSimulatorAPI) SplitText(text string) []string {
|
||||
return api.splitTextIntelligently(text, api.config.MinSegmentLen, api.config.MaxSegmentLen)
|
||||
}
|
||||
|
||||
// GenerateDelay 公开的延迟生成函数
|
||||
func (api *InputSimulatorAPI) GenerateDelay() int {
|
||||
return api.generateRandomDelay(api.config.MinDelayMs, api.config.MaxDelayMs)
|
||||
}
|
||||
@@ -558,7 +558,7 @@ func (ud *UIA2Driver) TouchByEvents(events []types.TouchEvent, opts ...option.Ac
|
||||
// direction: 滑动方向 ("up", "down", "left", "right")
|
||||
// startX, startY: 起始坐标
|
||||
// minDistance, maxDistance: 距离范围,如果相等则为固定距离,否则为随机距离
|
||||
func (ud *UIA2Driver) SwipeWithDirection(direction string, startX, startY, minDistance, maxDistance float64, opts ...option.ActionOption) error {
|
||||
func (ud *UIA2Driver) SIMSwipeWithDirection(direction string, startX, startY, minDistance, maxDistance float64, opts ...option.ActionOption) error {
|
||||
absStartX, absStartY, err := convertToAbsolutePoint(ud, startX, startY)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -610,7 +610,7 @@ func (ud *UIA2Driver) SwipeWithDirection(direction string, startX, startY, minDi
|
||||
// direction: 滑动方向 ("up", "down", "left", "right")
|
||||
// areaStartX, areaStartY, areaEndX, areaEndY: 区域范围(相对坐标)
|
||||
// minDistance, maxDistance: 距离范围,如果相等则为固定距离,否则为随机距离
|
||||
func (ud *UIA2Driver) SwipeInArea(direction string, areaStartX, areaStartY, areaEndX, areaEndY, minDistance, maxDistance float64, opts ...option.ActionOption) error {
|
||||
func (ud *UIA2Driver) SIMSwipeInArea(direction string, areaStartX, areaStartY, areaEndX, areaEndY, minDistance, maxDistance float64, opts ...option.ActionOption) error {
|
||||
// 转换区域坐标为绝对坐标
|
||||
absAreaStartX, absAreaStartY, err := convertToAbsolutePoint(ud, areaStartX, areaStartY)
|
||||
if err != nil {
|
||||
@@ -677,7 +677,7 @@ func (ud *UIA2Driver) SwipeInArea(direction string, areaStartX, areaStartY, area
|
||||
// SwipeFromPointToPoint 指定起始点和结束点进行滑动
|
||||
// startX, startY: 起始坐标(相对坐标)
|
||||
// endX, endY: 结束坐标(相对坐标)
|
||||
func (ud *UIA2Driver) SwipeFromPointToPoint(startX, startY, endX, endY float64, opts ...option.ActionOption) error {
|
||||
func (ud *UIA2Driver) SIMSwipeFromPointToPoint(startX, startY, endX, endY float64, opts ...option.ActionOption) error {
|
||||
// 转换起始点和结束点为绝对坐标
|
||||
absStartX, absStartY, err := convertToAbsolutePoint(ud, startX, startY)
|
||||
if err != nil {
|
||||
@@ -717,7 +717,7 @@ func (ud *UIA2Driver) SwipeFromPointToPoint(startX, startY, endX, endY float64,
|
||||
|
||||
// ClickAtPoint 点击相对坐标
|
||||
// x, y: 点击坐标(相对坐标)
|
||||
func (ud *UIA2Driver) ClickAtPoint(x, y float64, opts ...option.ActionOption) error {
|
||||
func (ud *UIA2Driver) SIMClickAtPoint(x, y float64, opts ...option.ActionOption) error {
|
||||
// 转换为绝对坐标
|
||||
absX, absY, err := convertToAbsolutePoint(ud, x, y)
|
||||
if err != nil {
|
||||
@@ -789,6 +789,71 @@ func (ud *UIA2Driver) Input(text string, opts ...option.ActionOption) (err error
|
||||
return
|
||||
}
|
||||
|
||||
// SIMInput 仿真输入函数,模拟人类分批输入行为
|
||||
// 将文本智能分割,英文单词和数字保持完整,中文按1-2个字符分割
|
||||
func (ud *UIA2Driver) SIMInput(text string, opts ...option.ActionOption) error {
|
||||
log.Info().Str("text", text).Msg("UIA2Driver.SIMInput")
|
||||
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建输入仿真器(使用默认配置)
|
||||
inputSimulator := simulation.NewInputSimulatorAPI(nil)
|
||||
|
||||
// 生成输入片段(使用智能分割算法,所有参数使用默认值)
|
||||
inputReq := simulation.InputRequest{
|
||||
Text: text,
|
||||
// MinSegmentLen, MaxSegmentLen, MinDelayMs, MaxDelayMs 使用默认值
|
||||
}
|
||||
|
||||
response := inputSimulator.GenerateInputSegments(inputReq)
|
||||
if !response.Success {
|
||||
return fmt.Errorf("failed to generate input segments: %s", response.Message)
|
||||
}
|
||||
|
||||
log.Info().Int("segments", response.Metrics.TotalSegments).
|
||||
Int("totalDelayMs", response.Metrics.TotalDelayMs).
|
||||
Int("estimatedTimeMs", response.Metrics.EstimatedTimeMs).
|
||||
Msg("Input segments generated")
|
||||
|
||||
// 逐个输入每个片段
|
||||
var segmentErr error
|
||||
for _, segment := range response.Segments {
|
||||
// 使用SendUnicodeKeys进行输入(内部已包含Session.POST请求)
|
||||
segmentErr = ud.SendUnicodeKeys(segment.Text, opts...)
|
||||
if segmentErr != nil {
|
||||
log.Info().Err(segmentErr).
|
||||
Msg("segments err")
|
||||
}
|
||||
|
||||
log.Debug().Str("segment", segment.Text).Int("index", segment.Index).
|
||||
Int("charLen", segment.CharLen).Msg("Successfully input segment")
|
||||
|
||||
// 如果有延迟时间,则等待
|
||||
if segment.DelayMs > 0 {
|
||||
time.Sleep(time.Duration(segment.DelayMs) * time.Millisecond)
|
||||
|
||||
log.Debug().Int("delayMs", segment.DelayMs).
|
||||
Msg("Delay between input segments")
|
||||
}
|
||||
}
|
||||
if segmentErr != nil {
|
||||
data := map[string]interface{}{
|
||||
"text": text,
|
||||
}
|
||||
option.MergeOptions(data, opts...)
|
||||
urlStr := fmt.Sprintf("/session/%s/keys", ud.Session.ID)
|
||||
_, err := ud.Session.POST(data, urlStr)
|
||||
return err
|
||||
}
|
||||
log.Info().Int("totalSegments", response.Metrics.TotalSegments).
|
||||
Int("actualDelayMs", response.Metrics.TotalDelayMs).
|
||||
Msg("SIMInput completed successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ud *UIA2Driver) SendUnicodeKeys(text string, opts ...option.ActionOption) (err error) {
|
||||
log.Info().Str("text", text).Msg("UIA2Driver.SendUnicodeKeys")
|
||||
// If the Unicode IME is not installed, fall back to the old interface.
|
||||
|
||||
Reference in New Issue
Block a user