Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b1d7d0f
feat: Add Android-specific Adb and SauceLabs providers
tustanivsky Nov 25, 2025
86e48f9
Fix comments and logs
tustanivsky Nov 25, 2025
927b225
Update Connect-Device func to use env var
tustanivsky Nov 26, 2025
4a55e0a
Add fallback for connect device func if target is empty
tustanivsky Nov 26, 2025
bd38cdc
Add SauceLabs session name override
tustanivsky Nov 26, 2025
26c1d8e
Update adb install command params (remove -r flag)
tustanivsky Nov 26, 2025
a3e4fce
Enforce intent format validation
tustanivsky Nov 26, 2025
7635aaa
Rework android package name extraction
tustanivsky Nov 26, 2025
da7d0da
Rename
tustanivsky Nov 26, 2025
c50bc0c
Fix adb devices discovery
tustanivsky Nov 26, 2025
cba2075
Fix some adb commands output checks
tustanivsky Nov 26, 2025
42c246a
Add unit tests for AndroidHelpers functions
tustanivsky Nov 27, 2025
7c0bb74
Fix formatting
tustanivsky Nov 27, 2025
3d5dd3e
Fix screenshot
tustanivsky Nov 27, 2025
842b08d
Add basic Android integration tests (needs pre-built .apk)
tustanivsky Nov 27, 2025
d9e7839
Allow intent arguments with "--"
limbonaut Nov 27, 2025
db0f6b1
Remove unnecessary initialWaitSeconds
limbonaut Nov 27, 2025
ec9f09d
Fix WaitForProcess PID gathering
limbonaut Nov 27, 2025
c1f33de
Rename "pid" var because analyzer complains about reserved name
limbonaut Nov 27, 2025
d3b0813
Add screenshot test
tustanivsky Nov 27, 2025
ff45311
Align naming
tustanivsky Nov 27, 2025
c0d3757
Add Android test app fixture
tustanivsky Nov 27, 2025
f41f9fe
Refactor SauceLabs provider (ios impl placeholder) + tests
tustanivsky Nov 28, 2025
8578335
Rename test
tustanivsky Nov 28, 2025
e9551b8
Rename adb provider
tustanivsky Nov 28, 2025
a715195
Clean up
tustanivsky Nov 28, 2025
b207c34
Add SauceLabs tests execution to CI
tustanivsky Nov 28, 2025
73d96cb
Fix secrets
tustanivsky Nov 28, 2025
92d93c4
Update docs
tustanivsky Nov 28, 2025
b2ff8d1
Fix readme
tustanivsky Nov 28, 2025
e05a67f
Remove unused force-stop command for Adb provider
tustanivsky Dec 2, 2025
31b012a
Update README.md
tustanivsky Dec 2, 2025
295c5d8
Move hardcoded time intervals to Timeouts property
tustanivsky Dec 2, 2025
baa451e
Fix adb command invocations
tustanivsky Dec 2, 2025
312965a
Remove command duplication for adb screenshot capture
tustanivsky Dec 2, 2025
922266c
Remove redundant screenshot command
tustanivsky Dec 2, 2025
cbb67b4
Rework Android package name extraction helper func
tustanivsky Dec 2, 2025
a2a1654
Remove package name helper fallback tests
tustanivsky Dec 2, 2025
4b229ff
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky Dec 2, 2025
1957e89
Update app-runner/Private/DeviceProviders/SauceLabsProvider.ps1
tustanivsky Dec 2, 2025
5fd78f6
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky Dec 2, 2025
b31283e
Update app-runner/Public/Connect-Device.ps1
tustanivsky Dec 2, 2025
5d2c793
Fix comment
tustanivsky Dec 2, 2025
da4faeb
Fix PR suggestion
tustanivsky Dec 2, 2025
d66a5cd
Update app-runner/Private/DeviceProviders/SauceLabsProvider.ps1
tustanivsky Dec 2, 2025
b88b632
Make SauceLabs tests mandatory in CI
tustanivsky Dec 2, 2025
e1a9413
Add Adb provider tests to CI
tustanivsky Dec 2, 2025
f7e5f5c
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky Dec 2, 2025
c4971d8
Update app-runner/README.md
tustanivsky Dec 2, 2025
1ec6480
Add null check for response
tustanivsky Dec 2, 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
194 changes: 194 additions & 0 deletions app-runner/Private/AndroidHelpers.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Android Helper Functions
# Shared utilities for Android device providers (ADB and SauceLabs)

<#
.SYNOPSIS
Converts an Android activity path into package name and activity name components.

.DESCRIPTION
Converts the ExecutablePath format used by Android apps: "package.name/activity.name"
Returns a hashtable with PackageName and ActivityName properties.

.PARAMETER ExecutablePath
The full activity path in format "package.name/activity.name"

.EXAMPLE
ConvertFrom-AndroidActivityPath "io.sentry.unreal.sample/com.epicgames.unreal.GameActivity"
Returns: @{ PackageName = "io.sentry.unreal.sample"; ActivityName = "com.epicgames.unreal.GameActivity" }

.EXAMPLE
ConvertFrom-AndroidActivityPath "com.example.app/.MainActivity"
Returns: @{ PackageName = "com.example.app"; ActivityName = ".MainActivity" }
#>
function ConvertFrom-AndroidActivityPath {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ExecutablePath
)

if ($ExecutablePath -notmatch '^([^/]+)/(.+)$') {
throw "ExecutablePath must be in format 'package.name/activity.name'. Got: $ExecutablePath"
}

return @{
PackageName = $matches[1]
ActivityName = $matches[2]
}
}

<#
.SYNOPSIS
Validates that Android Intent extras are in the correct 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

.PARAMETER Arguments
The arguments string to validate/format

.EXAMPLE
Test-IntentExtrasFormat "-e cmdline -crash-capture"
Returns: $true

.EXAMPLE
Test-IntentExtrasFormat "-e test true -ez debug false"
Returns: $true
#>
function Test-IntentExtrasFormat {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$Arguments
)

if ([string]::IsNullOrWhiteSpace($Arguments)) {
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."
}
Comment on lines 77 to 81
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure we really need to moderate this. Other flags should be allowed too. Plus, the canonical way to pass these flags is to use a single - for one-letter options, and -- for things like es, ez, and so on.

Copy link
Collaborator

@limbonaut limbonaut Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a fix for -- arguments in d9e7839, but maybe we could remove this whole validation? Wdyt?


return $true
}

<#
.SYNOPSIS
Extracts the package name from an APK file using aapt.

.DESCRIPTION
Attempts to extract the real package name from an APK file using aapt (Android Asset Packaging Tool).
If aapt is not available, falls back to using the APK filename without extension as a hint.

.PARAMETER ApkPath
Path to the APK file

.EXAMPLE
Get-ApkPackageName "MyApp.apk"
Returns: "com.example.myapp" (actual package name from AndroidManifest.xml)

.EXAMPLE
Get-ApkPackageName "SentryPlayground.apk"
Returns: "io.sentry.sample" (if aapt available) or "SentryPlayground" (filename fallback)

.NOTES
Requires aapt or aapt2 to be in PATH or Android SDK to be installed for accurate extraction.
Falls back to filename-based hint if aapt is unavailable.
#>
function Get-ApkPackageName {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ApkPath
)

if (-not (Test-Path $ApkPath)) {
throw "APK file not found: $ApkPath"
}

if ($ApkPath -notlike '*.apk') {
throw "File must be an .apk file. Got: $ApkPath"
}

# Try to use aapt to extract real package name
$aaptCmd = Get-Command aapt -ErrorAction SilentlyContinue
if (-not $aaptCmd) {
$aaptCmd = Get-Command aapt2 -ErrorAction SilentlyContinue
}

if ($aaptCmd) {
try {
Write-Debug "Using $($aaptCmd.Name) to extract package name from APK"
$PSNativeCommandUseErrorActionPreference = $false
$output = & $aaptCmd.Name dump badging $ApkPath 2>&1
$PSNativeCommandUseErrorActionPreference = $true

# Parse output for package name: package: name='com.example.app'
foreach ($line in $output) {
if ($line -match "package:\s+name='([^']+)'") {
$packageName = $matches[1]
Write-Debug "Extracted package name: $packageName"
return $packageName
}
}

Write-Warning "Failed to parse package name from aapt output, falling back to filename hint"
}
catch {
Write-Warning "Failed to execute aapt: $_. Falling back to filename hint"
}
}
else {
Write-Debug "aapt/aapt2 not found in PATH, falling back to filename hint"
}

# Fallback: Use APK filename without extension as package name hint
$hint = [System.IO.Path]::GetFileNameWithoutExtension($ApkPath)
Write-Warning "Using APK filename as package name hint: $hint (aapt not available for accurate extraction)"
return $hint
}

<#
.SYNOPSIS
Parses logcat output into structured format.

.DESCRIPTION
Converts raw logcat output (array of strings) into a consistent format
that can be used by test utilities like Get-EventIds.

.PARAMETER LogcatOutput
Array of logcat log lines (raw output from adb or SauceLabs)

.EXAMPLE
$logs = adb -s emulator-5554 logcat -d
Format-LogcatOutput $logs
#>
function Format-LogcatOutput {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[object[]]$LogcatOutput
)

if ($null -eq $LogcatOutput -or $LogcatOutput.Count -eq 0) {
return @()
}

# Ensure output is an array of strings
return @($LogcatOutput | ForEach-Object {
if ($null -ne $_) {
$_.ToString()
}
} | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
}
Loading