mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 07:26:55 +08:00
Merge branch 'fix-sleep' into 'master'
修复 sleep 相关工具函数,支持指定 startTime 参数 See merge request iesqa/httprunner!148
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0-250806
|
v5.0.0-250809
|
||||||
|
|||||||
@@ -284,8 +284,9 @@ func getSimulationDuration(params []float64) (milliseconds int64) {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleepStrict sleeps strict duration with given params
|
// sleepStrict sleeps for strict duration with optional start time correction
|
||||||
// startTime is used to correct sleep duration caused by process time
|
// If startTime is zero, acts as normal context-aware sleep
|
||||||
|
// If startTime is provided, corrects sleep duration by subtracting elapsed time
|
||||||
// ctx allows for cancellation during sleep
|
// ctx allows for cancellation during sleep
|
||||||
func sleepStrict(ctx context.Context, startTime time.Time, strictMilliseconds int64) {
|
func sleepStrict(ctx context.Context, startTime time.Time, strictMilliseconds int64) {
|
||||||
var elapsed int64
|
var elapsed int64
|
||||||
|
|||||||
+65
-22
@@ -15,7 +15,29 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToolSleep implements the sleep tool call.
|
// extractStartTimeMs extracts start_time_ms from MCP request arguments
|
||||||
|
// Returns time.Time (zero if not provided) and any conversion error
|
||||||
|
func extractStartTimeMs(request mcp.CallToolRequest) (time.Time, error) {
|
||||||
|
startTimeMs, ok := request.GetArguments()["start_time_ms"]
|
||||||
|
if !ok || startTimeMs == nil {
|
||||||
|
return time.Time{}, nil // Return zero time for normal sleep
|
||||||
|
}
|
||||||
|
|
||||||
|
var ms int64
|
||||||
|
switch v := startTimeMs.(type) {
|
||||||
|
case float64:
|
||||||
|
ms = int64(v)
|
||||||
|
case int64:
|
||||||
|
ms = v
|
||||||
|
case int:
|
||||||
|
ms = int64(v)
|
||||||
|
default:
|
||||||
|
return time.Time{}, fmt.Errorf("invalid start_time_ms type: %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.UnixMilli(ms), nil
|
||||||
|
}
|
||||||
|
|
||||||
type ToolSleep struct {
|
type ToolSleep struct {
|
||||||
// Return data fields - these define the structure of data returned by this tool
|
// Return data fields - these define the structure of data returned by this tool
|
||||||
Seconds float64 `json:"seconds" desc:"Duration in seconds that was slept"`
|
Seconds float64 `json:"seconds" desc:"Duration in seconds that was slept"`
|
||||||
@@ -33,6 +55,7 @@ func (t *ToolSleep) Description() string {
|
|||||||
func (t *ToolSleep) Options() []mcp.ToolOption {
|
func (t *ToolSleep) Options() []mcp.ToolOption {
|
||||||
return []mcp.ToolOption{
|
return []mcp.ToolOption{
|
||||||
mcp.WithNumber("seconds", mcp.Description("Number of seconds to sleep")),
|
mcp.WithNumber("seconds", mcp.Description("Number of seconds to sleep")),
|
||||||
|
mcp.WithNumber("start_time_ms", mcp.Description("Start time as Unix milliseconds for strict sleep calculation")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,16 +93,15 @@ func (t *ToolSleep) Implement() server.ToolHandlerFunc {
|
|||||||
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
|
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use context-aware sleep instead of blocking time.Sleep
|
// Extract start_time_ms and use sleepStrict for unified sleep logic
|
||||||
select {
|
startTime, err := extractStartTimeMs(request)
|
||||||
case <-time.After(duration):
|
if err != nil {
|
||||||
// Normal completion
|
return nil, err
|
||||||
case <-ctx.Done():
|
|
||||||
// Interrupted by context cancellation (interrupt signal, timeout, time limit)
|
|
||||||
log.Info().Msg("sleep interrupted by context cancellation")
|
|
||||||
// Don't return error - let the upper layer handle timeout/time limit logic
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
milliseconds := int64(actualSeconds * 1000)
|
||||||
|
sleepStrict(ctx, startTime, milliseconds)
|
||||||
|
|
||||||
message := fmt.Sprintf("Successfully slept for %v seconds", actualSeconds)
|
message := fmt.Sprintf("Successfully slept for %v seconds", actualSeconds)
|
||||||
returnData := ToolSleep{
|
returnData := ToolSleep{
|
||||||
Seconds: actualSeconds,
|
Seconds: actualSeconds,
|
||||||
@@ -91,9 +113,24 @@ func (t *ToolSleep) Implement() server.ToolHandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *ToolSleep) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
|
func (t *ToolSleep) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
|
||||||
arguments := map[string]any{
|
arguments := map[string]any{}
|
||||||
"seconds": action.Params,
|
|
||||||
|
var seconds float64
|
||||||
|
if param, ok := action.Params.(json.Number); ok {
|
||||||
|
seconds, _ = param.Float64()
|
||||||
|
arguments["seconds"] = seconds
|
||||||
|
} else if param, ok := action.Params.(int64); ok {
|
||||||
|
seconds = float64(param)
|
||||||
|
arguments["seconds"] = seconds
|
||||||
|
} else if sleepConfig, ok := action.Params.(SleepConfig); ok {
|
||||||
|
// When startTime is provided, pass both seconds and startTime
|
||||||
|
seconds = sleepConfig.Seconds
|
||||||
|
arguments["seconds"] = seconds
|
||||||
|
arguments["start_time_ms"] = sleepConfig.StartTime.UnixMilli()
|
||||||
|
} else {
|
||||||
|
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep params: %v", action.Params)
|
||||||
}
|
}
|
||||||
|
|
||||||
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
|
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +152,7 @@ func (t *ToolSleepMS) Description() string {
|
|||||||
func (t *ToolSleepMS) Options() []mcp.ToolOption {
|
func (t *ToolSleepMS) Options() []mcp.ToolOption {
|
||||||
return []mcp.ToolOption{
|
return []mcp.ToolOption{
|
||||||
mcp.WithNumber("milliseconds", mcp.Description("Number of milliseconds to sleep")),
|
mcp.WithNumber("milliseconds", mcp.Description("Number of milliseconds to sleep")),
|
||||||
|
mcp.WithNumber("start_time_ms", mcp.Description("Start time as Unix milliseconds for strict sleep calculation")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,16 +190,14 @@ func (t *ToolSleepMS) Implement() server.ToolHandlerFunc {
|
|||||||
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
|
return nil, fmt.Errorf("unsupported sleep duration type: %T", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use context-aware sleep instead of blocking time.Sleep
|
// Extract start_time_ms and use sleepStrict for unified sleep logic
|
||||||
select {
|
startTime, err := extractStartTimeMs(request)
|
||||||
case <-time.After(duration):
|
if err != nil {
|
||||||
// Normal completion
|
return nil, err
|
||||||
case <-ctx.Done():
|
|
||||||
// Interrupted by context cancellation (interrupt signal, timeout, time limit)
|
|
||||||
log.Info().Msg("sleep interrupted by context cancellation")
|
|
||||||
// Don't return error - let the upper layer handle timeout/time limit logic
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sleepStrict(ctx, startTime, actualMilliseconds)
|
||||||
|
|
||||||
message := fmt.Sprintf("Successfully slept for %d milliseconds", actualMilliseconds)
|
message := fmt.Sprintf("Successfully slept for %d milliseconds", actualMilliseconds)
|
||||||
returnData := ToolSleepMS{
|
returnData := ToolSleepMS{
|
||||||
Milliseconds: actualMilliseconds,
|
Milliseconds: actualMilliseconds,
|
||||||
@@ -173,17 +209,24 @@ func (t *ToolSleepMS) Implement() server.ToolHandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *ToolSleepMS) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
|
func (t *ToolSleepMS) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
|
||||||
|
arguments := map[string]any{}
|
||||||
|
|
||||||
var milliseconds int64
|
var milliseconds int64
|
||||||
if param, ok := action.Params.(json.Number); ok {
|
if param, ok := action.Params.(json.Number); ok {
|
||||||
milliseconds, _ = param.Int64()
|
milliseconds, _ = param.Int64()
|
||||||
|
arguments["milliseconds"] = milliseconds
|
||||||
} else if param, ok := action.Params.(int64); ok {
|
} else if param, ok := action.Params.(int64); ok {
|
||||||
milliseconds = param
|
milliseconds = param
|
||||||
|
arguments["milliseconds"] = milliseconds
|
||||||
|
} else if sleepConfig, ok := action.Params.(SleepConfig); ok {
|
||||||
|
// When startTime is provided, pass both milliseconds and startTime
|
||||||
|
milliseconds = sleepConfig.Milliseconds
|
||||||
|
arguments["milliseconds"] = milliseconds
|
||||||
|
arguments["start_time_ms"] = sleepConfig.StartTime.UnixMilli()
|
||||||
} else {
|
} else {
|
||||||
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep ms params: %v", action.Params)
|
return mcp.CallToolRequest{}, fmt.Errorf("invalid sleep ms params: %v", action.Params)
|
||||||
}
|
}
|
||||||
arguments := map[string]any{
|
|
||||||
"milliseconds": milliseconds,
|
|
||||||
}
|
|
||||||
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
|
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
package uixt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/httprunner/httprunner/v5/uixt/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToolSleep_ConvertActionToCallToolRequest(t *testing.T) {
|
||||||
|
tool := &ToolSleep{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
action option.MobileAction
|
||||||
|
expectedArgs map[string]any
|
||||||
|
shouldError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "json.Number parameter",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_Sleep,
|
||||||
|
Params: json.Number("3.5"),
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{"seconds": float64(3.5)},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int64 parameter",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_Sleep,
|
||||||
|
Params: int64(5),
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{"seconds": float64(5)},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SleepConfig with startTime",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_Sleep,
|
||||||
|
Params: SleepConfig{
|
||||||
|
StartTime: time.UnixMilli(1691234567890),
|
||||||
|
Seconds: 2.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{
|
||||||
|
"seconds": 2.5,
|
||||||
|
"start_time_ms": int64(1691234567890),
|
||||||
|
},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid parameter type",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_Sleep,
|
||||||
|
Params: "invalid",
|
||||||
|
},
|
||||||
|
expectedArgs: nil,
|
||||||
|
shouldError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
request, err := tool.ConvertActionToCallToolRequest(tt.action)
|
||||||
|
|
||||||
|
if tt.shouldError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
args := request.GetArguments()
|
||||||
|
for key, expectedValue := range tt.expectedArgs {
|
||||||
|
assert.Equal(t, expectedValue, args[key], "Argument %s mismatch", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToolSleepMS_ConvertActionToCallToolRequest(t *testing.T) {
|
||||||
|
tool := &ToolSleepMS{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
action option.MobileAction
|
||||||
|
expectedArgs map[string]any
|
||||||
|
shouldError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "json.Number parameter",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_SleepMS,
|
||||||
|
Params: json.Number("1500"),
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{"milliseconds": int64(1500)},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int64 parameter",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_SleepMS,
|
||||||
|
Params: int64(2000),
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{"milliseconds": int64(2000)},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SleepConfig with startTime",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_SleepMS,
|
||||||
|
Params: SleepConfig{
|
||||||
|
StartTime: time.UnixMilli(1691234567890),
|
||||||
|
Milliseconds: 3000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedArgs: map[string]any{
|
||||||
|
"milliseconds": int64(3000),
|
||||||
|
"start_time_ms": int64(1691234567890),
|
||||||
|
},
|
||||||
|
shouldError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid parameter type",
|
||||||
|
action: option.MobileAction{
|
||||||
|
Method: option.ACTION_SleepMS,
|
||||||
|
Params: "invalid",
|
||||||
|
},
|
||||||
|
expectedArgs: nil,
|
||||||
|
shouldError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
request, err := tool.ConvertActionToCallToolRequest(tt.action)
|
||||||
|
|
||||||
|
if tt.shouldError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
args := request.GetArguments()
|
||||||
|
for key, expectedValue := range tt.expectedArgs {
|
||||||
|
assert.Equal(t, expectedValue, args[key], "Argument %s mismatch", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSleepStrictTiming(t *testing.T) {
|
||||||
|
// Test that strict sleep properly adjusts for elapsed time
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Simulate some processing time
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Test sleepStrict with the start time
|
||||||
|
testStart := time.Now()
|
||||||
|
sleepStrict(ctx, startTime, 200) // 200ms total duration
|
||||||
|
actualElapsed := time.Since(testStart)
|
||||||
|
|
||||||
|
// Should sleep approximately 150ms (200ms - 50ms already elapsed)
|
||||||
|
// Allow some tolerance for timing variations
|
||||||
|
expectedSleep := 150 * time.Millisecond
|
||||||
|
assert.Greater(t, actualElapsed, expectedSleep/2, "Sleep too short")
|
||||||
|
assert.Less(t, actualElapsed, expectedSleep*2, "Sleep too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSleepCancellation(t *testing.T) {
|
||||||
|
// Test that sleep respects context cancellation
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Cancel after 50ms
|
||||||
|
go func() {
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
sleepStrict(ctx, time.Time{}, 500) // Try to sleep 500ms
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
|
// Should be cancelled after ~50ms, not sleep full 500ms
|
||||||
|
assert.Less(t, elapsed, 200*time.Millisecond, "Sleep was not properly cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSleepStrictWithZeroTime(t *testing.T) {
|
||||||
|
// Test sleepStrict behaves like normal sleep when startTime is zero
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
sleepStrict(ctx, time.Time{}, 100) // 100ms, no start time
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
|
// Should sleep full duration
|
||||||
|
expectedSleep := 100 * time.Millisecond
|
||||||
|
assert.Greater(t, elapsed, expectedSleep/2, "Sleep too short")
|
||||||
|
assert.Less(t, elapsed, expectedSleep*2, "Sleep too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSleepStrictWithPastStartTime(t *testing.T) {
|
||||||
|
// Test sleepStrict skips sleep when elapsed time exceeds duration
|
||||||
|
startTime := time.Now().Add(-300 * time.Millisecond) // 300ms ago
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
sleepStrict(ctx, startTime, 200) // Want 200ms total, but 300ms already elapsed
|
||||||
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
|
// Should skip sleep entirely
|
||||||
|
assert.Less(t, elapsed, 50*time.Millisecond, "Should have skipped sleep")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonNumberHandling(t *testing.T) {
|
||||||
|
// Test that json.Number is correctly handled in different scenarios
|
||||||
|
|
||||||
|
// Test float json.Number
|
||||||
|
floatNumber := json.Number("3.14")
|
||||||
|
floatVal, err := floatNumber.Float64()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 3.14, floatVal)
|
||||||
|
|
||||||
|
// Test int json.Number
|
||||||
|
intNumber := json.Number("1500")
|
||||||
|
intVal, err := intNumber.Int64()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(1500), intVal)
|
||||||
|
|
||||||
|
// Test invalid json.Number
|
||||||
|
invalidNumber := json.Number("invalid")
|
||||||
|
_, err = invalidNumber.Float64()
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
@@ -137,42 +137,6 @@ func TestIOSTouchByEvents(t *testing.T) {
|
|||||||
t.Logf("Successfully executed touch events: %d events processed", len(events))
|
t.Logf("Successfully executed touch events: %d events processed", len(events))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIOSTouchByEvents(t *testing.T) {
|
|
||||||
driver := setupWDADriverExt(t)
|
|
||||||
|
|
||||||
// Example touch event data as provided
|
|
||||||
touchEventData := `1752649131556,401.20703,1191.3164,2,1.0,0.03529412,400.20703,400.3164,111586196,111586196,1,0,0
|
|
||||||
1752649131595,402.913,1185.0792,2,1.0,0.039215688,300.913,300.0792,111586196,111586236,1,0,2
|
|
||||||
1752649131612,410.60825,1164.3806,2,1.0,0.03529412,250.60825,250.3806,111586196,111586250,1,0,2
|
|
||||||
1752649131907,709.1758,523.34766,2,1.0,0.03529412,200.1758,200.34766,111586196,111586546,1,0,1`
|
|
||||||
|
|
||||||
// Parse touch events
|
|
||||||
events, err := ParseTouchEvents(touchEventData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ParseTouchEvents failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check first event
|
|
||||||
firstEvent := events[0]
|
|
||||||
if firstEvent.Action != 0 { // ACTION_DOWN
|
|
||||||
t.Errorf("Expected first event action to be 0 (ACTION_DOWN), got %d", firstEvent.Action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check last event
|
|
||||||
lastEvent := events[len(events)-1]
|
|
||||||
if lastEvent.Action != 1 { // ACTION_UP
|
|
||||||
t.Errorf("Expected last event action to be 1 (ACTION_UP), got %d", lastEvent.Action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use TouchByEvents with parsed events
|
|
||||||
err = driver.IDriver.(*WDADriver).TouchByEvents(events)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("TouchByEvents failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Successfully executed touch events: %d events processed", len(events))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTouchEventParsing(t *testing.T) {
|
func TestTouchEventParsing(t *testing.T) {
|
||||||
// Test single touch event parsing
|
// Test single touch event parsing
|
||||||
singleEventData := "1752646457403,456.78418,1574.0195,7,1.0,0.016666668,504.78418,1721.0195,924451292,924451292,1,0,0"
|
singleEventData := "1752646457403,456.78418,1574.0195,7,1.0,0.016666668,504.78418,1721.0195,924451292,924451292,1,0,0"
|
||||||
|
|||||||
Reference in New Issue
Block a user