mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(ios): three unbacked reads report not-supported instead of success
RecentLogs returned an empty slice, Metrics a zeroed sample and Health a hardcoded Ready: true, which are the same values a working read returns. Every property over state.logs held vacuously on iOS with nothing saying the logs were never read. driver.ErrNotSupported makes the silence representable.
This commit is contained in:
1 parent
08df960e0f
commit
1de179995b
3 files changed
+56
-16
No files matched your search
@@ -21,6 +21,14 @@ var ErrGestureUndelivered = errors.New("gesture reached no element")
|
|||||||
// ErrGestureUndelivered and the runner records it as an unresolved selector.
|
// ErrGestureUndelivered and the runner records it as an unresolved selector.
|
||||||
var ErrSelectorMatchedNothing = errors.New("selector matched no element")
|
var ErrSelectorMatchedNothing = errors.New("selector matched no element")
|
||||||
|
|
||||||
|
// ErrNotSupported reports a contract method the driver has no way to answer on
|
||||||
|
// its platform. It is neither a device fault nor an observation: a caller that
|
||||||
|
// reads the zero value as one records a reading it never took, which is what
|
||||||
|
// lets a property over a channel the driver never opened hold vacuously with
|
||||||
|
// nothing in the run's output saying so. Callers check it with errors.Is and
|
||||||
|
// record that the read did not happen.
|
||||||
|
var ErrNotSupported = errors.New("driver: capability not supported")
|
||||||
|
|
||||||
// DeviceDriver abstracts the platform-specific UI automation backend. v0.1
|
// DeviceDriver abstracts the platform-specific UI automation backend. v0.1
|
||||||
// surface matches proto/driverpb/driver.proto. The sidecar implementation
|
// surface matches proto/driverpb/driver.proto. The sidecar implementation
|
||||||
// lives under driver/sidecar; the web implementation under driver/chrome;
|
// lives under driver/sidecar; the web implementation under driver/chrome;
|
||||||
@@ -50,15 +58,23 @@ type DeviceDriver interface {
|
|||||||
// independent reads can land on different frames during transitions.
|
// independent reads can land on different frames during transitions.
|
||||||
Snapshot(ctx context.Context) (string, Image, error)
|
Snapshot(ctx context.Context) (string, Image, error)
|
||||||
// RecentLogs returns log entries at or after `since`, filtered to
|
// RecentLogs returns log entries at or after `since`, filtered to
|
||||||
// `minLevel` or above. An empty minLevel defaults to "E".
|
// `minLevel` or above. An empty minLevel defaults to "E". A driver with
|
||||||
|
// no log source returns ErrNotSupported rather than an empty slice: the
|
||||||
|
// two are the same answer to a spec reading state.logs, and only one of
|
||||||
|
// them means the app logged nothing.
|
||||||
RecentLogs(ctx context.Context, since time.Time, minLevel string) ([]LogEntry, error)
|
RecentLogs(ctx context.Context, since time.Time, minLevel string) ([]LogEntry, error)
|
||||||
|
|
||||||
WaitForIdle(ctx context.Context, duration time.Duration) error
|
WaitForIdle(ctx context.Context, duration time.Duration) error
|
||||||
|
// Health reports whether the backend is attached and serving. A driver
|
||||||
|
// that runs no readiness check returns ErrNotSupported; Ready is a
|
||||||
|
// verdict, so reporting it true without one tells the caller a check
|
||||||
|
// passed that never ran.
|
||||||
Health(ctx context.Context) (Health, error)
|
Health(ctx context.Context) (Health, error)
|
||||||
// Metrics samples the app's CPU and memory at the time of the call.
|
// Metrics samples the app's CPU and memory at the time of the call.
|
||||||
// CPUPercent is percent of a single core (multi-core apps can exceed
|
// CPUPercent is percent of a single core (multi-core apps can exceed
|
||||||
// 100). HeapBytes is resident set size; TotalMemoryBytes includes
|
// 100). HeapBytes is resident set size; TotalMemoryBytes includes
|
||||||
// native allocations.
|
// native allocations. A driver that cannot sample returns
|
||||||
|
// ErrNotSupported rather than a zeroed sample.
|
||||||
Metrics(ctx context.Context, bundleID string) (Metrics, error)
|
Metrics(ctx context.Context, bundleID string) (Metrics, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1107,21 +1107,24 @@ func (d *Driver) WaitForIdle(ctx context.Context, _ time.Duration) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecentLogs returns no entries: the companion log RPC is a follow-up, so v1
|
// RecentLogs, Metrics and Health have no companion RPC behind them yet, so
|
||||||
// reports an empty slice rather than failing. Every property reading state.logs
|
// each reports driver.ErrNotSupported. An empty log slice, a zeroed sample and
|
||||||
// therefore holds vacuously on iOS and nothing says so; closing it means
|
// a hardcoded Ready are the same values a working read can return, so answering
|
||||||
// tailing idb's streaming log RPC and mapping os_log levels onto the
|
// with them told the runner three checks had passed that never ran and left
|
||||||
// single-letter scale driver.LogEntry declares.
|
// every property reading state.logs holding vacuously with nothing saying so.
|
||||||
|
// Closing them means tailing the streaming log RPC and mapping os_log levels
|
||||||
|
// onto the single-letter scale driver.LogEntry declares, sampling the app
|
||||||
|
// process, and probing the bridge for readiness.
|
||||||
func (d *Driver) RecentLogs(_ context.Context, _ time.Time, _ string) ([]driver.LogEntry, error) {
|
func (d *Driver) RecentLogs(_ context.Context, _ time.Time, _ string) ([]driver.LogEntry, error) {
|
||||||
return []driver.LogEntry{}, nil
|
return nil, driver.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) Metrics(_ context.Context, _ string) (driver.Metrics, error) {
|
func (d *Driver) Metrics(_ context.Context, _ string) (driver.Metrics, error) {
|
||||||
return driver.Metrics{}, nil
|
return driver.Metrics{}, driver.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) Health(_ context.Context) (driver.Health, error) {
|
func (d *Driver) Health(_ context.Context) (driver.Health, error) {
|
||||||
return driver.Health{Ready: true, Platform: "ios"}, nil
|
return driver.Health{}, driver.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForegroundApp reports the foreground app. It returns the app under test when
|
// ForegroundApp reports the foreground app. It returns the app under test when
|
||||||
|
|||||||
@@ -699,14 +699,35 @@ func TestForegroundAppFallsBackToOtherRunningUserApp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthReportsIOS(t *testing.T) {
|
// The three reads the companion has no RPC for must say so. Answering an empty
|
||||||
|
// log slice, a zeroed sample or Ready reports three checks as having passed
|
||||||
|
// that never ran, and a spec's log properties then hold on evidence nobody
|
||||||
|
// collected.
|
||||||
|
func TestUnbackedReadsReportNotSupported(t *testing.T) {
|
||||||
d := newTestDriver(&fakeCompanion{})
|
d := newTestDriver(&fakeCompanion{})
|
||||||
health, err := d.Health(context.Background())
|
|
||||||
if err != nil {
|
logs, err := d.RecentLogs(context.Background(), time.Time{}, "E")
|
||||||
t.Fatalf("Health: %v", err)
|
if !errors.Is(err, driver.ErrNotSupported) {
|
||||||
|
t.Errorf("RecentLogs err = %v, want driver.ErrNotSupported", err)
|
||||||
}
|
}
|
||||||
if !health.Ready || health.Platform != "ios" {
|
if len(logs) != 0 {
|
||||||
t.Fatalf("Health = %+v, want ready ios", health)
|
t.Errorf("RecentLogs returned %d entries alongside its error", len(logs))
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics, err := d.Metrics(context.Background(), "com.fixture")
|
||||||
|
if !errors.Is(err, driver.ErrNotSupported) {
|
||||||
|
t.Errorf("Metrics err = %v, want driver.ErrNotSupported", err)
|
||||||
|
}
|
||||||
|
if metrics != (driver.Metrics{}) {
|
||||||
|
t.Errorf("Metrics = %+v, want the zero sample", metrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
health, err := d.Health(context.Background())
|
||||||
|
if !errors.Is(err, driver.ErrNotSupported) {
|
||||||
|
t.Errorf("Health err = %v, want driver.ErrNotSupported", err)
|
||||||
|
}
|
||||||
|
if health.Ready {
|
||||||
|
t.Errorf("Health = %+v: the driver reports a readiness check it never ran", health)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user