Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3e5924a
Fix argument splitting
limbonaut Dec 8, 2025
663dac8
Use proper parameter on iOS vs Android
limbonaut Dec 8, 2025
7dd6fe1
Implement CopyDeviceItem (iOS tested)
limbonaut Dec 9, 2025
a748051
Add Android-specific diagnostics
limbonaut Dec 10, 2025
89c0ca1
Add log file override support to RunApplication
limbonaut Dec 10, 2025
4aee5ae
Undo changes in AdbProvider
limbonaut Dec 10, 2025
04087e8
Restore comment
limbonaut Dec 10, 2025
72f28c0
Restore another comment
limbonaut Dec 10, 2025
cac36d6
Refactor RunApplication to use optional parameter instead of overloads
limbonaut Dec 10, 2025
c629164
Update MockDeviceProvider RunApplication signature
limbonaut Dec 10, 2025
c88f48c
Remove outdated warning - args are supported on iOS
limbonaut Dec 10, 2025
6e0824b
Refactor API to pass arguments as arrays instead of strings
limbonaut Dec 13, 2025
2b15f36
Extract argument conversion to standalone function
limbonaut Dec 15, 2025
1910626
Fix app name resolution logic in SauceLabsProvider
limbonaut Dec 15, 2025
6352e57
Use Write-Warning
limbonaut Dec 15, 2025
fc1ccac
Update AndroidHelpers.Tests.ps1
limbonaut Dec 15, 2025
8310373
Fix typo in test filename
limbonaut Dec 15, 2025
bb84b7c
Remove debug preference configuration from tests
limbonaut Dec 15, 2025
5546947
Simplify Appium request body construction
limbonaut Dec 15, 2025
57ae3b5
Fix tests
limbonaut Dec 15, 2025
ebb2875
Print log grouped in GHA
limbonaut Dec 15, 2025
d28c68e
Fix indentation and improve log output handling
limbonaut Dec 15, 2025
ad71c2a
Warn when LogFilePath parameter is unsupported Add warnings to device
limbonaut Dec 15, 2025
8b18f2d
Remove argument guarantees and document expectations
limbonaut Dec 15, 2025
a45e24d
Revert tests
limbonaut Dec 15, 2025
e20da39
Revert more tests
limbonaut Dec 16, 2025
dbbcac8
Fix test
limbonaut Dec 16, 2025
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
64 changes: 42 additions & 22 deletions app-runner/Private/AndroidHelpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,45 +39,65 @@ function ConvertFrom-AndroidActivityPath {

<#
.SYNOPSIS
Validates that Android Intent extras are in the correct format.
Validates that an array of arguments can be safely converted to Intent extras format.

.DESCRIPTION
Android Intent extras should be passed in the format understood by `am start`.
This function validates and optionally formats the arguments string.

Common Intent extra formats:
-e key value String extra
-es key value String extra (explicit)
-ez key true|false Boolean extra
-ei key value Integer extra
-el key value Long extra
Validates each element of an argument array to ensure they form valid Intent extras
when combined. This prevents issues where individual elements are valid but the
combined string breaks Intent extras format.

.PARAMETER Arguments
The arguments string to validate/format
Array of string arguments to validate

.EXAMPLE
Test-IntentExtrasFormat "-e cmdline -crash-capture"
Test-IntentExtrasArray @('-e', 'key', 'value')
Returns: $true

.EXAMPLE
Test-IntentExtrasFormat "-e test true -ez debug false"
Returns: $true
Test-IntentExtrasArray @('-e', 'key with spaces', 'value')
Returns: $true (will be quoted properly)

.EXAMPLE
Test-IntentExtrasArray @('invalid', 'format')
Throws error for invalid format
#>
function Test-IntentExtrasFormat {
function Test-IntentExtrasArray {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$Arguments
[string[]]$Arguments
)

if ([string]::IsNullOrWhiteSpace($Arguments)) {
if (-not $Arguments -or $Arguments.Count -eq 0) {
return $true
}

# Intent extras must start with flags: -e, -es, -ez, -ei, -el, -ef, -eu, etc.
# Followed by at least one whitespace and additional content
if ($Arguments -notmatch '^--?[a-z]{1,2}\s+') {
throw "Invalid Intent extras format: '$Arguments'. Must start with flags like -e, -es, -ez, -ei, -el, etc. followed by key-value pairs."
# Only validate specific patterns we understand and can verify
# Don't throw errors on unknown patterns - just validate what we know
$knownKeyValueFlags = @('-e', '-es', '--es', '-ez', '--ez', '-ei', '--ei', '-el', '--el')

for ($i = 0; $i -lt $Arguments.Count; $i++) {
$currentArg = $Arguments[$i]

if ($knownKeyValueFlags -contains $currentArg) {
# For known key-value flags, ensure proper structure
if ($i + 2 -ge $Arguments.Count) {
throw "Invalid Intent extras format: Flag '$currentArg' must be followed by key and value. Missing arguments."
}

$key = $Arguments[$i + 1]
$value = $Arguments[$i + 2]

# For boolean flags, validate the value
if ($currentArg -in @('-ez', '--ez') -and $value -notin @('true', 'false')) {
throw "Invalid Intent extras format: Boolean flag '$currentArg' requires 'true' or 'false' value, got: '$value'"
}

# Skip the key and value we just validated
$i += 2
}
# For all other arguments (including single tokens like --grant-read-uri-permission),
# just continue - don't validate what we don't understand
}

return $true
Expand Down Expand Up @@ -132,7 +152,7 @@ function Get-ApkPackageName {
}

Write-Debug "Using $($aaptCmd.Name) to extract package name from APK"

try {
$PSNativeCommandUseErrorActionPreference = $false
$output = & $aaptCmd.Name dump badging $ApkPath 2>&1
Expand Down
19 changes: 13 additions & 6 deletions app-runner/Private/DeviceProviders/AdbProvider.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,23 @@ class AdbProvider : DeviceProvider {
}
}

[hashtable] RunApplication([string]$ExecutablePath, [string]$Arguments) {
[hashtable] RunApplication([string]$ExecutablePath, [string[]]$Arguments, [string]$LogFilePath = $null) {
# LogFilePath parameter ignored in this implementation
Write-Debug "$($this.Platform): Running application: $ExecutablePath"

if (-not ([string]::IsNullOrEmpty($LogFilePath))) {
Write-Warning "LogFilePath parameter is not supported on this platform."
}

# Parse ExecutablePath: "package.name/activity.name"
$parsed = ConvertFrom-AndroidActivityPath -ExecutablePath $ExecutablePath
$packageName = $parsed.PackageName
$activityName = $parsed.ActivityName
$this.CurrentPackageName = $packageName

# Validate Intent extras format
if ($Arguments) {
Test-IntentExtrasFormat -Arguments $Arguments | Out-Null
if ($Arguments -and $Arguments.Count -gt 0) {
Test-IntentExtrasArray -Arguments $Arguments | Out-Null
}

$timeoutSeconds = $this.Timeouts['run-timeout']
Expand All @@ -221,11 +226,13 @@ class AdbProvider : DeviceProvider {

# Launch activity
Write-Host "Launching: $ExecutablePath" -ForegroundColor Cyan
if ($Arguments) {
Write-Host " Arguments: $Arguments" -ForegroundColor Cyan

$argumentsString = $Arguments -join ' '
if ($argumentsString) {
Write-Host " Arguments: $argumentsString" -ForegroundColor Cyan
}

$launchOutput = $this.InvokeCommand('launch', @($this.DeviceSerial, $ExecutablePath, $Arguments))
$launchOutput = $this.InvokeCommand('launch', @($this.DeviceSerial, $ExecutablePath, $argumentsString))

# Join output to string first since -match on arrays returns matching elements, not boolean
if (($launchOutput -join "`n") -match 'Error') {
Expand Down
11 changes: 8 additions & 3 deletions app-runner/Private/DeviceProviders/DeviceProvider.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -372,14 +372,19 @@ class DeviceProvider {
return @{}
}

[hashtable] RunApplication([string]$ExecutablePath, [string]$Arguments) {
[hashtable] RunApplication([string]$ExecutablePath, [string[]]$Arguments, [string]$LogFilePath = $null) {
Write-Debug "$($this.Platform): Running application: $ExecutablePath with arguments: $Arguments"

$command = $this.BuildCommand('launch', @($ExecutablePath, $Arguments))
if (-not ([string]::IsNullOrEmpty($LogFilePath))) {
Write-Warning "LogFilePath parameter is not supported on this platform."
}

$argumentsString = $Arguments -join ' '
$command = $this.BuildCommand('launch', @($ExecutablePath, $argumentsString))
return $this.InvokeApplicationCommand($command, $ExecutablePath, $Arguments)
}

[hashtable] InvokeApplicationCommand([BuiltCommand]$builtCommand, [string]$ExecutablePath, [string]$Arguments) {
[hashtable] InvokeApplicationCommand([BuiltCommand]$builtCommand, [string]$ExecutablePath, [string[]]$Arguments) {
Write-Debug "$($this.Platform): Invoking $($builtCommand.Command)"

$result = $null
Expand Down
2 changes: 1 addition & 1 deletion app-runner/Private/DeviceProviders/MockDeviceProvider.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class MockDeviceProvider : DeviceProvider {
}
}

[hashtable] RunApplication([string]$ExecutablePath, [string]$Arguments) {
[hashtable] RunApplication([string]$ExecutablePath, [string[]]$Arguments, [string]$LogFilePath = $null) {
Write-Debug "Mock: Running application $ExecutablePath with args: $Arguments"

$this.MockConfig.AppRunning = $true
Expand Down
Loading