Skip to content

Commit

Permalink
allow passing args and envs to app launch (#498)
Browse files Browse the repository at this point in the history
* allow passing args and envs to app launch

* add integration test
  • Loading branch information
aluedeke authored Oct 14, 2024
1 parent 16cafba commit 11611f4
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 5 deletions.
18 changes: 18 additions & 0 deletions ios/instruments/processcontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ func (p *ProcessControl) LaunchApp(bundleID string, my_opts map[string]any) (uin
return p.StartProcess(bundleID, env, []interface{}{}, opts)
}

// LaunchApp launches the app with the given bundleID on the given device.LaunchApp
// It returns the PID of the created app process.
func (p *ProcessControl) LaunchAppWithArgs(bundleID string, my_args []interface{}, my_env map[string]any, my_opts map[string]any) (uint64, error) {
opts := map[string]interface{}{
"StartSuspendedKey": uint64(0),
"KillExisting": uint64(0),
}
maps.Copy(opts, my_opts)
// Xcode sends all these, no idea if we need them for sth. later.
//"CA_ASSERT_MAIN_THREAD_TRANSACTIONS": "0", "CA_DEBUG_TRANSACTIONS": "0", "LLVM_PROFILE_FILE": "/dev/null", "METAL_DEBUG_ERROR_MODE": "0", "METAL_DEVICE_WRAPPER_TYPE": "1",
//"OS_ACTIVITY_DT_MODE": "YES", "SQLITE_ENABLE_THREAD_ASSERTIONS": "1", "__XPC_LLVM_PROFILE_FILE": "/dev/null"
// NSUnbufferedIO seems to make the app send its logs via instruments using the outputReceived:fromProcess:atTime: selector
// We'll supply per default to get logs
env := map[string]interface{}{"NSUnbufferedIO": "YES"}
maps.Copy(env, my_env)
return p.StartProcess(bundleID, env, my_args, opts)
}

func (p *ProcessControl) Close() error {
return p.conn.Close()
}
Expand Down
50 changes: 48 additions & 2 deletions ios/instruments/processcontrol_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,68 @@ func TestLaunchAndKill(t *testing.T) {
}
const weatherAppBundleID = "com.apple.weather"
pControl, err := instruments.NewProcessControl(device)
defer pControl.Close()
if !assert.NoError(t, err) {
t.Fatal(err)
}
defer pControl.Close()

pid, err := pControl.LaunchApp(weatherAppBundleID, nil)
if !assert.NoError(t, err) {
return
}
assert.Greater(t, pid, uint64(0))

service, err := instruments.NewDeviceInfoService(device)
if !assert.NoError(t, err) {
return
}
defer service.Close()

processList, err := service.ProcessList()
if !assert.NoError(t, err) {
return
}
found := false
for _, proc := range processList {
if proc.Pid == pid {
found = true
}
}
if !found {
t.Errorf("could not find weather app with pid %d in proclist: %+v", pid, processList)
return
}
err = pControl.KillProcess(pid)
assert.NoError(t, err)
}

func TestLaunchWithArgsAndKill(t *testing.T) {
device, err := ios.GetDevice("")
if err != nil {
t.Fatal(err)
}
const weatherAppBundleID = "com.apple.weather"
pControl, err := instruments.NewProcessControl(device)
if !assert.NoError(t, err) {
t.Fatal(err)
}
defer pControl.Close()

var args = []interface{}{"-AppleLanguages", "(de-DE)"}
var env = map[string]interface{}{"SomeRandomValue": "YES"}

pid, err := pControl.LaunchAppWithArgs(weatherAppBundleID, args, env, nil)
if !assert.NoError(t, err) {
return
}
assert.Greater(t, pid, uint64(0))

service, err := instruments.NewDeviceInfoService(device)
if !assert.NoError(t, err) {
return
}
defer service.Close()

processList, err := service.ProcessList()
if !assert.NoError(t, err) {
return
Expand All @@ -49,5 +96,4 @@ func TestLaunchAndKill(t *testing.T) {
}
err = pControl.KillProcess(pid)
assert.NoError(t, err)
return
}
29 changes: 26 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Usage:
ios install --path=<ipaOrAppFolder> [options]
ios uninstall <bundleID> [options]
ios apps [--system] [--all] [--list] [--filesharing] [options]
ios launch <bundleID> [--wait] [--kill-existing] [options]
ios launch <bundleID> [--wait] [--kill-existing] [--arg=<a>]... [--env=<e>]... [options]
ios kill (<bundleID> | --pid=<processID> | --process=<processName>) [options]
ios runtest [--bundle-id=<bundleid>] [--test-runner-bundle-id=<testrunnerbundleid>] [--xctest-config=<xctestconfig>] [--log-output=<file>] [--xctest] [--test-to-run=<tests>]... [--test-to-skip=<tests>]... [--env=<e>]... [options]
ios runwda [--bundleid=<bundleid>] [--testrunnerbundleid=<testbundleid>] [--xctestconfig=<xctestconfig>] [--log-output=<file>] [--arg=<a>]... [--env=<e>]... [options]
Expand Down Expand Up @@ -221,7 +221,7 @@ The commands work as following:
ios install --path=<ipaOrAppFolder> [options] Specify a .app folder or an installable ipa file that will be installed.
ios pcap [options] [--pid=<processID>] [--process=<processName>] Starts a pcap dump of network traffic, use --pid or --process to filter specific processes.
ios apps [--system] [--all] [--list] [--filesharing] Retrieves a list of installed applications. --system prints out preinstalled system apps. --all prints all apps, including system, user, and hidden apps. --list only prints bundle ID, bundle name and version number. --filesharing only prints apps which enable documents sharing.
ios launch <bundleID> [--wait] [--kill-existing] [options] Launch app with the bundleID on the device. Get your bundle ID from the apps command. --wait keeps the connection open if you want logs.
ios launch <bundleID> [--wait] [--kill-existing] [--arg=<a>]... [--env=<e>]... [options] Launch app with the bundleID on the device. Get your bundle ID from the apps command. --wait keeps the connection open if you want logs.
ios kill (<bundleID> | --pid=<processID> | --process=<processName>) [options] Kill app with the specified bundleID, process id, or process name on the device.
ios runtest [--bundle-id=<bundleid>] [--test-runner-bundle-id=<testbundleid>] [--xctest-config=<xctestconfig>] [--log-output=<file>] [--xctest] [--test-to-run=<tests>]... [--test-to-skip=<tests>]... [--env=<e>]... [options] Run a XCUITest. If you provide only bundle-id go-ios will try to dynamically create test-runner-bundle-id and xctest-config.
> If you provide '-' as log output, it prints resuts to stdout.
Expand Down Expand Up @@ -817,7 +817,9 @@ The commands work as following:
if bKillExisting {
opts["KillExisting"] = 1
} // end if
pid, err := pControl.LaunchApp(bundleID, opts)
args := toArgs(arguments["--arg"].([]string))
envs := toEnvs(arguments["--env"].([]string))
pid, err := pControl.LaunchAppWithArgs(bundleID, args, envs, opts)
exitIfError("launch app command failed", err)
log.WithFields(log.Fields{"pid": pid}).Info("Process launched")
if wait {
Expand Down Expand Up @@ -1277,6 +1279,27 @@ func instrumentsCommand(device ios.DeviceEntry, arguments docopt.Opts) bool {
return b
}

func toArgs(argsIn []string) []interface{} {
args := []interface{}{}
for _, arg := range argsIn {
args = append(args, arg)
}
return args
}

func toEnvs(envsIn []string) map[string]interface{} {
env := map[string]interface{}{}

for _, entrystring := range envsIn {
entry := strings.Split(entrystring, "=")
key := entry[0]
value := entry[1]
env[key] = value
}

return env
}

func crashCommand(device ios.DeviceEntry, arguments docopt.Opts) bool {
b, _ := arguments.Bool("crash")
if b {
Expand Down

0 comments on commit 11611f4

Please sign in to comment.