-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add Android support (adb, SauceLabs) #30
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
Merged
Merged
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 86e48f9
Fix comments and logs
tustanivsky 927b225
Update Connect-Device func to use env var
tustanivsky 4a55e0a
Add fallback for connect device func if target is empty
tustanivsky bd38cdc
Add SauceLabs session name override
tustanivsky 26c1d8e
Update adb install command params (remove -r flag)
tustanivsky a3e4fce
Enforce intent format validation
tustanivsky 7635aaa
Rework android package name extraction
tustanivsky da7d0da
Rename
tustanivsky c50bc0c
Fix adb devices discovery
tustanivsky cba2075
Fix some adb commands output checks
tustanivsky 42c246a
Add unit tests for AndroidHelpers functions
tustanivsky 7c0bb74
Fix formatting
tustanivsky 3d5dd3e
Fix screenshot
tustanivsky 842b08d
Add basic Android integration tests (needs pre-built .apk)
tustanivsky d9e7839
Allow intent arguments with "--"
limbonaut db0f6b1
Remove unnecessary initialWaitSeconds
limbonaut ec9f09d
Fix WaitForProcess PID gathering
limbonaut c1f33de
Rename "pid" var because analyzer complains about reserved name
limbonaut d3b0813
Add screenshot test
tustanivsky ff45311
Align naming
tustanivsky c0d3757
Add Android test app fixture
tustanivsky f41f9fe
Refactor SauceLabs provider (ios impl placeholder) + tests
tustanivsky 8578335
Rename test
tustanivsky e9551b8
Rename adb provider
tustanivsky a715195
Clean up
tustanivsky b207c34
Add SauceLabs tests execution to CI
tustanivsky 73d96cb
Fix secrets
tustanivsky 92d93c4
Update docs
tustanivsky b2ff8d1
Fix readme
tustanivsky e05a67f
Remove unused force-stop command for Adb provider
tustanivsky 31b012a
Update README.md
tustanivsky 295c5d8
Move hardcoded time intervals to Timeouts property
tustanivsky baa451e
Fix adb command invocations
tustanivsky 312965a
Remove command duplication for adb screenshot capture
tustanivsky 922266c
Remove redundant screenshot command
tustanivsky cbb67b4
Rework Android package name extraction helper func
tustanivsky a2a1654
Remove package name helper fallback tests
tustanivsky 4b229ff
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky 1957e89
Update app-runner/Private/DeviceProviders/SauceLabsProvider.ps1
tustanivsky 5fd78f6
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky b31283e
Update app-runner/Public/Connect-Device.ps1
tustanivsky 5d2c793
Fix comment
tustanivsky da4faeb
Fix PR suggestion
tustanivsky d66a5cd
Update app-runner/Private/DeviceProviders/SauceLabsProvider.ps1
tustanivsky b88b632
Make SauceLabs tests mandatory in CI
tustanivsky e1a9413
Add Adb provider tests to CI
tustanivsky f7e5f5c
Update app-runner/Private/DeviceProviders/AdbProvider.ps1
tustanivsky c4971d8
Update app-runner/README.md
tustanivsky 1ec6480
Add null check for response
tustanivsky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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." | ||
| } | ||
|
|
||
| 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 | ||
tustanivsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| <# | ||
| .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($_) }) | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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 likees,ez, and so on.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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?