diff --git a/devices/devicekit/requests.go b/devices/devicekit/requests.go index abda773..1e3de63 100644 --- a/devices/devicekit/requests.go +++ b/devices/devicekit/requests.go @@ -34,7 +34,12 @@ type jsonRPCError struct { Message string `json:"message"` } -const defaultRPCTimeout = 10 * time.Second +// a var so tests can shorten it +var defaultRPCTimeout = 10 * time.Second + +// dumpUITimeout gives the agent time to snapshot a heavy screen, while keeping +// the request below the http client's own timeout +const dumpUITimeout = 55 * time.Second func (c *DeviceKitClient) CallRPC(method string, params any) (json.RawMessage, error) { return c.CallRPCWithTimeout(method, params, defaultRPCTimeout) diff --git a/devices/devicekit/source.go b/devices/devicekit/source.go index d93933c..e295ed4 100644 --- a/devices/devicekit/source.go +++ b/devices/devicekit/source.go @@ -129,7 +129,7 @@ func filterSourceElements(source sourceTreeElement) []types.ScreenElement { func (c *DeviceKitClient) GetSourceRaw() (any, error) { startTime := time.Now() - result, err := c.CallRPC("device.dump.ui", map[string]string{"format": "raw"}) + result, err := c.CallRPCWithTimeout("device.dump.ui", map[string]string{"format": "raw"}, dumpUITimeout) if err != nil { return nil, fmt.Errorf("failed to get source: %w", err) } @@ -148,7 +148,7 @@ func (c *DeviceKitClient) GetSourceRaw() (any, error) { func (c *DeviceKitClient) GetSourceElements() ([]types.ScreenElement, error) { startTime := time.Now() - result, err := c.CallRPC("device.dump.ui", map[string]string{"format": "json"}) + result, err := c.CallRPCWithTimeout("device.dump.ui", map[string]string{"format": "json"}, dumpUITimeout) if err != nil { return nil, err } diff --git a/devices/devicekit/source_test.go b/devices/devicekit/source_test.go index 8481014..22bdb9c 100644 --- a/devices/devicekit/source_test.go +++ b/devices/devicekit/source_test.go @@ -1,7 +1,10 @@ package devicekit import ( + "net/http" + "net/http/httptest" "testing" + "time" "github.com/mobile-next/mobilecli/types" ) @@ -543,3 +546,36 @@ func TestFilterSourceElementsDoesNotMarkNonSwitchAsChecked(t *testing.T) { t.Errorf("expected checked to be unset, got %v", *output[0].Checked) } } + +// Snapshotting a heavy screen can take the agent longer than the default RPC +// timeout; dump ui has to wait for it instead of failing with "context +// deadline exceeded", see https://github.com/mobile-next/mobilecli/issues/448 +func TestDumpUIWaitsForAnAgentSlowerThanTheDefaultRPCTimeout(t *testing.T) { + previous := defaultRPCTimeout + defaultRPCTimeout = 50 * time.Millisecond + t.Cleanup(func() { defaultRPCTimeout = previous }) + + slowAgent := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + time.Sleep(200 * time.Millisecond) + _, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"type":"XCUIElementTypeApplication","rect":{"x":0,"y":0,"width":390,"height":844},"children":[{"type":"XCUIElementTypeButton","label":"Done","rect":{"x":16,"y":62,"width":44,"height":44}}]}}`)) + })) + defer slowAgent.Close() + + client := NewDeviceKitClient(slowAgent.URL) + + if _, err := client.CallRPC("device.info", nil); err == nil { + t.Fatal("expected the agent to be slower than the default RPC timeout") + } + + elements, err := client.GetSourceElements() + if err != nil { + t.Fatalf("GetSourceElements: %v", err) + } + if len(elements) != 1 || elementLabel(elements[0]) != "Done" { + t.Errorf("expected the Done button, got %+v", elements) + } + + if _, err := client.GetSourceRaw(); err != nil { + t.Fatalf("GetSourceRaw: %v", err) + } +} diff --git a/server/deadline_test.go b/server/deadline_test.go index 41d80ec..0371912 100644 --- a/server/deadline_test.go +++ b/server/deadline_test.go @@ -16,6 +16,7 @@ func TestExtendedWriteDeadlineForSlowMethods(t *testing.T) { "device.apps.install": 3 * time.Minute, "device.apps.uninstall": 3 * time.Minute, "device.screenrecord.stop": 35 * time.Second, + "device.dump.ui": 60 * time.Second, } for method, want := range slowMethods { diff --git a/server/server.go b/server/server.go index 7271c62..e42b434 100644 --- a/server/server.go +++ b/server/server.go @@ -294,6 +294,8 @@ func extendedWriteDeadline(method string) (time.Duration, bool) { return 3 * time.Minute, true case "device.screenrecord.stop": return 35 * time.Second, true + case "device.dump.ui": + return 60 * time.Second, true } return 0, false }