Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow passing args and envs to app launch #498

Merged
merged 2 commits into from
Oct 14, 2024
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
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
Loading