Skip to content
Draft
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
17 changes: 17 additions & 0 deletions app-runner/Private/DeviceProviders/AdbProvider.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class AdbProvider : DeviceProvider {
# App execution
'launch' = @('adb', '-s {0} shell am start -n {1} {2} -W')
'pidof' = @('adb', '-s {0} shell pidof {1}')
'force-stop' = @('adb', '-s {0} shell am force-stop {1}')

# Logging
'logcat' = @('adb', '-s {0} logcat -d')
Expand Down Expand Up @@ -218,6 +219,9 @@ class AdbProvider : DeviceProvider {

$startTime = Get-Date

# Otherwise `am start` brings a still-running (possibly hung) instance to the front instead of starting a new one.
$this.StopApp($packageName)

# Clear logcat before launch
Write-Debug "$($this.Platform): Clearing logcat"
$this.InvokeCommand('logcat-clear', @($this.DeviceSerial))
Expand Down Expand Up @@ -289,6 +293,10 @@ class AdbProvider : DeviceProvider {
$logCache = @($this.InvokeCommand('logcat-pid', @($this.DeviceSerial, $appPID)))
Write-Host "Retrieved $($logCache.Count) log lines" -ForegroundColor Cyan

if (-not $processExited) {
$this.StopApp($packageName)
}

Write-Host "Android doesn't report exit codes via adb so exit code is always NULL"
$exitCode = $null
}
Expand All @@ -308,6 +316,15 @@ class AdbProvider : DeviceProvider {
}
}

[void] StopApp([string]$packageName) {
try {
$this.InvokeCommand('force-stop', @($this.DeviceSerial, $packageName)) | Out-Null
}
catch {
Write-Warning "$($this.Platform): Failed to force-stop ${packageName}: $_"
}
}

# Helper method to wait for process with retry
[string] WaitForProcess([string]$packageName, [int]$timeoutSeconds) {
Write-Debug "$($this.Platform): Waiting for process: $packageName"
Expand Down
69 changes: 69 additions & 0 deletions app-runner/Tests/AdbRunApplication.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
$ErrorActionPreference = 'Stop'

BeforeAll {
. "$PSScriptRoot\..\Private\DeviceProviders\DeviceProvider.ps1"
. "$PSScriptRoot\..\Private\DeviceProviders\AdbProvider.ps1"

Invoke-Expression @'
class FakeAdbProvider : AdbProvider {
[System.Collections.Generic.List[string]]$Calls = [System.Collections.Generic.List[string]]::new()
[bool]$Hangs = $false
[bool]$ForceStopFails = $false
[int]$PidofCalls = 0

FakeAdbProvider() : base() {
$this.DeviceSerial = 'emulator-5554'
$this.Timeouts['run-timeout'] = 1
$this.Timeouts['pid-retry'] = 1
$this.Timeouts['process-check-interval'] = 0
}

[object] InvokeCommand([string]$action, [object[]]$parameters) {
$this.Calls.Add($action)
switch ($action) {
'force-stop' { if ($this.ForceStopFails) { throw 'adb: device offline' } }
'launch' { return 'Status: ok' }
'pidof' {
$this.PidofCalls++
if ($this.Hangs -or $this.PidofCalls -eq 1) { return '4056' }
return $null
}
'logcat-pid' { return @('10-20 08:14:04.782 4056 4056 I IL2CPP : JNI_OnLoad') }
}
return $null
}
}
'@
}

Describe 'AdbProvider.RunApplication' -Tag 'Unit' {
It 'Force-stops the package before launching it' {
$provider = [FakeAdbProvider]::new()

$provider.RunApplication('com.example.app/.MainActivity', @(), $null, $null) | Out-Null

$provider.Calls[0] | Should -Be 'force-stop'
$provider.Calls.IndexOf('force-stop') | Should -BeLessThan $provider.Calls.IndexOf('launch')
@($provider.Calls | Where-Object { $_ -eq 'force-stop' }).Count | Should -Be 1
}

It 'Force-stops the package after a run timeout, once its logs are read' {
$provider = [FakeAdbProvider]::new()
$provider.Hangs = $true

$result = $provider.RunApplication('com.example.app/.MainActivity', @(), $null, $null)

$provider.Calls[-1] | Should -Be 'force-stop'
$provider.Calls[-2] | Should -Be 'logcat-pid'
$result.Output | Should -Contain '10-20 08:14:04.782 4056 4056 I IL2CPP : JNI_OnLoad'
}

It 'Still launches when force-stop fails' {
$provider = [FakeAdbProvider]::new()
$provider.ForceStopFails = $true

$provider.RunApplication('com.example.app/.MainActivity', @(), $null, $null) 3>$null | Out-Null

$provider.Calls | Should -Contain 'launch'
}
}
Loading