Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Fixed
- **DeviceLab iOS: `launchApp` with the default `all: allow` no longer resets the app's permissions on a simulator.** It reset every permission and then granted `all` with `simctl`, but simctl's contacts grant does not take (iOS 18.6 simulator) and it has no notifications service, so a grant made another way (the user's tap on the prompt, applesimutils, an earlier run) was lost on every launch and the app asked again: an app that shows a permission onboarding when a permission is undecided opened on it after every `launchApp`. An explicit `permissions:` map still resets first, so permissions it doesn't name stay undecided.
- **`childOf` and `containsChild` follow the element tree, as in Maestro, on DeviceLab, UIAutomator2, Appium and WDA.** They compared rectangles, so any element inside the anchor's bounds counted as its child. A list row behind an overlay that covers it (a search-results list drawn over the full list) "was in" the overlay, and `assertVisible: {id: title, childOf: {id: search_results}}` passed while the results list was empty. Both filters now walk the parent chain; an anchor without tree links (rebuilt from a relative selector) still uses bounds. The DeviceLab iOS driver already did this.
- **UIAutomator2 `hideKeyboard` no longer trusts a single "keyboard hidden" read.** While the keyboard is still coming up, `dumpsys window InputMethod` can report it not shown and Appium's hide_keyboard can answer 404, so the step returned without pressing BACK and the keyboard stayed over the next target. The driver now treats the keyboard as hidden only when two reads 300 ms apart agree, both before and after Appium's call, and still presses BACK only while the keyboard is shown.
- **WDA keeps enough idle connections for its own parallel reads.** The driver reads an element's name, rect, text and displayed at once, and a tap looks an element up four ways at once, but Go's default transport keeps two idle connections per host, so every burst closed two connections and opened two new ones. Through a forwarded port to a physical iPhone, new connections opened together fail with EOF and are sent again, which costs time on every step. The WDA client now keeps up to eight.
Expand Down
19 changes: 17 additions & 2 deletions pkg/driver/devicelab_ios/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,14 @@ func (d *Driver) applyLaunchPermissions(bid string, perms map[string]string) {
if len(perms) == 0 {
perms = map[string]string{"all": "allow"}
}
if _, err := d.runSimctl("privacy", d.udid, "reset", "all", bid); err != nil {
logger.Warn("launchApp: permission reset failed: %v", err)
// With all:allow nothing is meant to stay "not determined", so the reset can only lose grants, and
// simctl cannot put all of them back: its contacts grant does not take (iOS 18.6 simulator) and it has
// no notifications service. Grants made another way (the user's tap on the prompt, applesimutils, an
// earlier run) are kept instead of being asked for again on every launch.
if !hasOnlyAllAllow(perms) {
if _, err := d.runSimctl("privacy", d.udid, "reset", "all", bid); err != nil {
logger.Warn("launchApp: permission reset failed: %v", err)
}
}
set := map[string]string{}
for name, value := range perms {
Expand All @@ -349,6 +355,15 @@ func (d *Driver) applyLaunchPermissions(bid string, perms map[string]string) {
}
}

// hasOnlyAllAllow reports whether perms is just all:allow, launchApp's default.
func hasOnlyAllAllow(perms map[string]string) bool {
if len(perms) != 1 {
return false
}
value, ok := perms["all"]
return ok && strings.EqualFold(strings.TrimSpace(value), "allow")
}

// applyPermissions sets each permission with `simctl privacy`, one service
// per call, in a stable order. Names iOS gives the host no control over
// (notifications, faceid) are logged and skipped.
Expand Down
4 changes: 4 additions & 0 deletions pkg/driver/devicelab_ios/lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func TestLaunchAppDefaultsAndErrors(t *testing.T) {
if !sl.has("privacy SIM-1 grant all com.x") {
t.Fatalf("default all:allow missing: %v", sl.calls)
}
// A reset would lose grants that simctl cannot give back (contacts, notifications).
if sl.has("privacy SIM-1 reset") {
t.Fatalf("default all:allow must not reset permissions: %v", sl.calls)
}
if sl.has("terminate") {
t.Fatal("stopApp: false must not terminate")
}
Expand Down
Loading