diff --git a/build-tools/automation/ApkTestsHelix.proj b/build-tools/automation/ApkTestsHelix.proj new file mode 100644 index 00000000000..efbd28c0b9e --- /dev/null +++ b/build-tools/automation/ApkTestsHelix.proj @@ -0,0 +1,32 @@ + + + msbuild + false + true + tests/android/apk-tests/ + Android APK Tests - Helix + 00:30:00 + + + + + + + $(HelixTestRunName) + + + $(ApkTestsHelixWorkItemTimeout) + + + + + + + + + + diff --git a/build-tools/automation/apk-tests-helix/ApkTestsHelix.Tests.ps1 b/build-tools/automation/apk-tests-helix/ApkTestsHelix.Tests.ps1 new file mode 100644 index 00000000000..80bdb45f0c5 --- /dev/null +++ b/build-tools/automation/apk-tests-helix/ApkTestsHelix.Tests.ps1 @@ -0,0 +1,69 @@ +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Assert-Equal +{ + param ( + [Parameter(Mandatory)] + [object] $Expected, + + [Parameter(Mandatory)] + [object] $Actual, + + [Parameter(Mandatory)] + [string] $Message + ) + + if ($Expected -ne $Actual) { + throw "$Message Expected '$Expected', got '$Actual'." + } +} + +$root = Join-Path $PSScriptRoot '.test-output' +Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction Ignore +try { + $workItem = Join-Path $root 'work-items\sample' + $platformTools = Join-Path $root 'platform-tools' + New-Item -ItemType Directory -Force -Path $workItem, $platformTools | Out-Null + Set-Content -LiteralPath (Join-Path $workItem 'app.apk') -Value 'apk' + Set-Content -LiteralPath (Join-Path $platformTools 'adb.exe') -Value 'adb' + [pscustomobject] @{ + name = 'sample' + displayName = 'Sample APK Tests' + packageName = 'example.tests' + instrumentation = 'example.tests.TestInstrumentation' + } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $workItem 'case.json') + + $propsPath = Join-Path $root 'items.props' + & (Join-Path $PSScriptRoot 'prepare-apk-test-helix-submission.ps1') ` + -WorkItemsDirectory (Join-Path $root 'work-items') ` + -ItemsPropsPath $propsPath ` + -ResultsDirectory (Join-Path $root 'results') ` + -PlatformToolsDirectory $platformTools ` + -TargetMinutes 15 + + [xml] $props = Get-Content -LiteralPath $propsPath -Raw + $item = $props.Project.ItemGroup._ApkTestHelixWorkItem + Assert-Equal 'apk-tests-sample' ([string] $item.Include) 'Work item name should be deterministic.' + Assert-Equal 'results.trx;console.log;logcat.log;device-state.log;case.json;work-item-error.log' ([string] $item.DownloadFilesFromResults) 'Result files should be downloaded.' + + $script = Get-Content -LiteralPath (Join-Path $workItem 'run-apk-tests.ps1') -Raw + if (-not $script.Contains('$packageName = ''example.tests''') -or + -not $script.Contains('$instrumentation = ''example.tests.TestInstrumentation''') -or + -not $script.Contains('INSTRUMENTATION_RESULT: resultsPath=')) { + throw 'Generated APK test script is missing required instrumentation values.' + } + if (-not (Test-Path -LiteralPath (Join-Path $workItem 'platform-tools\adb.exe') -PathType Leaf)) { + throw 'Platform tools were not copied into the work item payload.' + } + $tokens = $null + $errors = $null + [Management.Automation.Language.Parser]::ParseFile((Join-Path $workItem 'run-apk-tests.ps1'), [ref] $tokens, [ref] $errors) | Out-Null + if ($errors.Count -gt 0) { + throw ($errors | ForEach-Object Message | Out-String) + } + + Write-Host 'ApkTestsHelix tests passed.' +} finally { + Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction Ignore +} diff --git a/build-tools/automation/apk-tests-helix/README.md b/build-tools/automation/apk-tests-helix/README.md new file mode 100644 index 00000000000..a6a72ce8f2c --- /dev/null +++ b/build-tools/automation/apk-tests-helix/README.md @@ -0,0 +1,18 @@ +# APK tests in Helix prototype + +This guarded prototype builds the existing on-device APK test flavors once on a +Windows Azure Pipelines agent, then submits one deterministic Helix work item per flavor. Existing +measurements put every device test invocation below the configurable 15-minute target, +so splitting an APK's NUnit inventory further would add installation overhead without +improving the tail. + +Each small work item contains a signed APK, Android `platform-tools`, its +package/instrumentation metadata, and a PowerShell runner, matching the proven +PR #12020 payload layout. +The runner installs the APK, invokes `am instrument`, pulls the test-generated TRX, +captures logcat/device state, and returns failure when instrumentation or any test +fails. + +The prototype uses `windows.11.amd64.android.open`, following the MAUI R2R Android +Helix implementation. The existing macOS emulator lanes remain enabled for same-build +inventory and outcome comparison. diff --git a/build-tools/automation/apk-tests-helix/prepare-apk-test-helix-submission.ps1 b/build-tools/automation/apk-tests-helix/prepare-apk-test-helix-submission.ps1 new file mode 100644 index 00000000000..35be92618a1 --- /dev/null +++ b/build-tools/automation/apk-tests-helix/prepare-apk-test-helix-submission.ps1 @@ -0,0 +1,201 @@ +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [string] $WorkItemsDirectory, + + [Parameter(Mandatory)] + [string] $ItemsPropsPath, + + [Parameter(Mandatory)] + [string] $ResultsDirectory, + + [Parameter(Mandatory)] + [string] $PlatformToolsDirectory, + + [ValidateRange(1, 1440)] + [int] $TargetMinutes = 15 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Copy-PayloadDirectory +{ + param ( + [Parameter(Mandatory)] + [string] $Source, + + [Parameter(Mandatory)] + [string] $Destination + ) + + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + if ($IsWindows) { + & robocopy $Source $Destination /E /COPY:DAT /DCOPY:DAT /R:2 /W:1 /NFL /NDL /NJH /NJS /NP + if ($LASTEXITCODE -gt 7) { + throw "robocopy failed with exit code $LASTEXITCODE while copying '$Source'." + } + $global:LASTEXITCODE = 0 + } else { + & cp -a "$Source/." "$Destination/" + if ($LASTEXITCODE -ne 0) { + throw "cp failed with exit code $LASTEXITCODE while copying '$Source'." + } + } +} + +function Escape-PowerShellSingleQuotedString +{ + param ( + [Parameter(Mandatory)] + [string] $Value + ) + + return $Value.Replace("'", "''") +} + +$workItemsFullPath = [IO.Path]::GetFullPath($WorkItemsDirectory) +$itemsPropsFullPath = [IO.Path]::GetFullPath($ItemsPropsPath) +$resultsFullPath = [IO.Path]::GetFullPath($ResultsDirectory) + +if (-not (Test-Path -LiteralPath $workItemsFullPath -PathType Container)) { + throw "Work item directory '$workItemsFullPath' does not exist." +} +if (-not (Test-Path -LiteralPath $PlatformToolsDirectory -PathType Container)) { + throw "Android platform-tools directory '$PlatformToolsDirectory' does not exist." +} + +New-Item -ItemType Directory -Force -Path $resultsFullPath | Out-Null + +$cases = [System.Collections.Generic.List[object]]::new() +foreach ($caseFile in Get-ChildItem -LiteralPath $workItemsFullPath -Filter 'case.json' -Recurse | Sort-Object FullName) { + $case = Get-Content -LiteralPath $caseFile.FullName -Raw | ConvertFrom-Json + $payloadDirectory = Split-Path -Parent $caseFile.FullName + $apkPath = Join-Path $payloadDirectory 'app.apk' + if (-not (Test-Path -LiteralPath $apkPath -PathType Leaf)) { + throw "APK payload '$apkPath' does not exist." + } + Copy-PayloadDirectory -Source $PlatformToolsDirectory -Destination (Join-Path $payloadDirectory 'platform-tools') + + $packageName = Escape-PowerShellSingleQuotedString ([string] $case.packageName) + $instrumentation = Escape-PowerShellSingleQuotedString ([string] $case.instrumentation) + $script = @' +$ErrorActionPreference = 'Stop' +$upload = $env:HELIX_WORKITEM_UPLOAD_ROOT +$adb = Join-Path $PSScriptRoot 'platform-tools\adb.exe' +$apk = Join-Path $PSScriptRoot 'app.apk' +$packageName = '__PACKAGE_NAME__' +$instrumentation = '__INSTRUMENTATION__' +$exitCode = 1 + +if ([string]::IsNullOrWhiteSpace($upload)) { + throw 'HELIX_WORKITEM_UPLOAD_ROOT is not set.' +} +New-Item -ItemType Directory -Force -Path $upload | Out-Null +Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'case.json') -Destination (Join-Path $upload 'case.json') + +try { + & $adb devices -l | Tee-Object -FilePath (Join-Path $upload 'adb-devices.log') + if ($LASTEXITCODE -ne 0) { + throw "adb devices failed with exit code $LASTEXITCODE." + } + + & $adb uninstall $packageName *> $null + & $adb install -r $apk + if ($LASTEXITCODE -ne 0) { + throw "adb install failed with exit code $LASTEXITCODE." + } + + & $adb logcat -c + $instrumentationOutput = @(& $adb shell am instrument -w -r "$packageName/$instrumentation" 2>&1) + $instrumentationOutput | Tee-Object -FilePath (Join-Path $upload 'console.log') + if ($LASTEXITCODE -ne 0) { + throw "adb instrument failed with exit code $LASTEXITCODE." + } + + $resultPathMatch = [regex]::Match(($instrumentationOutput -join "`n"), '(?m)^INSTRUMENTATION_RESULT: resultsPath=(?.+)$') + if (-not $resultPathMatch.Success) { + throw 'Instrumentation did not report a TRX result path.' + } + + $deviceResultsPath = $resultPathMatch.Groups['path'].Value.Trim() + $localResultsPath = Join-Path $upload 'results.trx' + & $adb pull $deviceResultsPath $localResultsPath + if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $localResultsPath -PathType Leaf)) { + throw "Failed to pull TRX from '$deviceResultsPath'." + } + + [xml] $trx = Get-Content -LiteralPath $localResultsPath -Raw + $failed = @($trx.GetElementsByTagName('UnitTestResult') | Where-Object { $_.GetAttribute('outcome') -eq 'Failed' }) + if ($failed.Count -gt 0) { + throw "$($failed.Count) on-device test(s) failed." + } + + $exitCode = 0 +} catch { + "ERROR: $($_.Exception.Message)" | Tee-Object -FilePath (Join-Path $upload 'work-item-error.log') -Append +} finally { + @( + '===== get-state =====' + (& $adb get-state 2>&1) + '===== boot completion =====' + (& $adb shell getprop sys.boot_completed 2>&1) + '===== disk =====' + (& $adb shell df /data 2>&1) + '===== packages =====' + (& $adb shell pm list packages -3 2>&1) + ) | Set-Content -LiteralPath (Join-Path $upload 'device-state.log') + & $adb logcat -d -b all *> (Join-Path $upload 'logcat.log') + & $adb uninstall $packageName *> $null +} + +exit $exitCode +'@ + $script = $script.Replace('__PACKAGE_NAME__', $packageName).Replace('__INSTRUMENTATION__', $instrumentation) + [IO.File]::WriteAllText((Join-Path $payloadDirectory 'run-apk-tests.ps1'), $script, [Text.UTF8Encoding]::new($false)) + + $cases.Add([pscustomobject] @{ + Name = [string] $case.name + DisplayName = [string] $case.displayName + PayloadDirectory = $payloadDirectory + }) +} + +if ($cases.Count -eq 0) { + throw "No case.json files were found under '$workItemsFullPath'." +} + +$settings = [Xml.XmlWriterSettings]::new() +$settings.Indent = $true +$settings.Encoding = [Text.UTF8Encoding]::new($false) +$writer = [Xml.XmlWriter]::Create($itemsPropsFullPath, $settings) +try { + $writer.WriteStartElement('Project') + $writer.WriteStartElement('ItemGroup') + foreach ($case in $cases) { + $writer.WriteStartElement('_ApkTestHelixWorkItem') + $writer.WriteAttributeString('Include', "apk-tests-$($case.Name)") + $writer.WriteElementString('PayloadDirectory', $case.PayloadDirectory) + $writer.WriteElementString('Command', 'powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File run-apk-tests.ps1') + $writer.WriteElementString('DownloadFilesFromResults', 'results.trx;console.log;logcat.log;device-state.log;case.json;work-item-error.log') + $writer.WriteEndElement() + } + $writer.WriteEndElement() + $writer.WriteEndElement() +} finally { + $writer.Dispose() +} + +[pscustomobject] @{ + targetMinutes = $TargetMinutes + workItemCount = $cases.Count + cases = $cases | ForEach-Object { + [pscustomobject] @{ + name = $_.Name + displayName = $_.DisplayName + } + } +} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $resultsFullPath 'work-item-generation.json') -Encoding utf8NoBOM + +Write-Host "Prepared $($cases.Count) APK test Helix work items." +Write-Host "##vso[task.setvariable variable=ApkTestsHelixItemsProps]$itemsPropsFullPath" diff --git a/build-tools/automation/azure-pipelines-public.yaml b/build-tools/automation/azure-pipelines-public.yaml index eb12c44be1b..648191f6e01 100644 --- a/build-tools/automation/azure-pipelines-public.yaml +++ b/build-tools/automation/azure-pipelines-public.yaml @@ -29,6 +29,18 @@ parameters: values: - 'true' - 'false' +- name: enableApkTestsHelixPrototype + displayName: Enable APK tests in Helix prototype + type: boolean + default: false +- name: apkTestsHelixTargetMinutes + displayName: APK test Helix target minutes + type: number + default: 15 +- name: apkTestsHelixOnly + displayName: Run only the APK test Helix prototype after builds + type: boolean + default: false # Repository resources resources: @@ -157,6 +169,27 @@ stages: nugetArtifactName: $(LinuxNuGetArtifactName) use1ESTemplate: false +- ${{ if eq(parameters.enableApkTestsHelixPrototype, true) }}: + - stage: apk_tests_helix + displayName: APK Tests Helix Prototype + dependsOn: + - mac_build + condition: and(succeeded(), or(ne(variables['SkipTestStages'], 'true'), eq('${{ parameters.apkTestsHelixOnly }}', 'true'))) + jobs: + - job: apk_tests_helix + displayName: Android APK Tests > Helix + pool: + name: $(NetCorePublicPoolName) + demands: + - ImageOverride -equals $(WindowsPoolImageNetCorePublic) + timeoutInMinutes: 240 + workspace: + clean: all + steps: + - template: /build-tools/automation/yaml-templates/run-apk-tests-in-helix.yaml + parameters: + targetMinutes: ${{ parameters.apkTestsHelixTargetMinutes }} + # Package Tests Stage - ${{ if ne(variables.SkipTestStages, 'true') }}: - template: /build-tools/automation/yaml-templates/stage-package-tests.yaml diff --git a/build-tools/automation/yaml-templates/run-apk-tests-in-helix.yaml b/build-tools/automation/yaml-templates/run-apk-tests-in-helix.yaml new file mode 100644 index 00000000000..cdcf9ee4bc4 --- /dev/null +++ b/build-tools/automation/yaml-templates/run-apk-tests-in-helix.yaml @@ -0,0 +1,227 @@ +parameters: + targetMinutes: 15 + workItemTimeout: '00:30:00' + helixQueue: windows.11.amd64.android.open + testCases: + - name: debug + displayName: Mono.Android.NET_Tests-Debug + configuration: Debug + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '' + - name: release + displayName: Mono.Android.NET_Tests-Release + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '' + - name: no-aab + displayName: Mono.Android.NET_Tests-NoAab + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '-p:TestsFlavor=NoAab' + - name: coreclr + displayName: Mono.Android.NET_Tests-CoreCLR + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '-p:TestsFlavor=CoreCLR -p:UseMonoRuntime=false' + - name: mono + displayName: Mono.Android.NET_Tests-Mono + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '-p:UseMonoRuntime=true -p:_DisableCheckForUnsupportedMonoMobileRuntime=true' + - name: coreclr-trimmable + displayName: Mono.Android.NET_Tests-CoreCLRTrimmable + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '-p:TestsFlavor=CoreCLRTrimmable -p:AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false' + - name: nativeaot + displayName: Mono.Android.NET_Tests-NativeAOT + configuration: $(XA.Build.Configuration) + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + artifactName: Mono.Android.NET_Tests-Signed.apk + packageName: Xamarin.Android.RuntimeTests + instrumentation: xamarin.android.runtimetests.TestInstrumentation + extraBuildArguments: '-p:TestsFlavor=NativeAOT -p:PublishAot=true' + - name: jcwgen + displayName: Xamarin.Android.JcwGen_Tests + configuration: $(XA.Build.Configuration) + project: tests/CodeGen-Binding/Xamarin.Android.JcwGen-Tests/Xamarin.Android.JcwGen-Tests.csproj + artifactName: Xamarin.Android.JcwGen_Tests-Signed.apk + packageName: Xamarin.Android.JcwGen_Tests + instrumentation: xamarin.android.jcwgentests.TestInstrumentation + extraBuildArguments: '' + +steps: +- template: /build-tools/automation/yaml-templates/setup-test-environment-public.yaml + parameters: + useAgentJdkPath: false + use1ESTemplate: false + +- pwsh: | + $ErrorActionPreference = 'Stop' + $androidSdk = @( + $env:ANDROID_HOME + $env:ANDROID_SDK_ROOT + $env:AndroidSdkDirectory + $(if ($env:USERPROFILE) { Join-Path $env:USERPROFILE 'android-toolchain\sdk' }) + $(if ($env:HOME) { Join-Path $env:HOME 'android-toolchain/sdk' }) + ) | Where-Object { $_ -and (Test-Path (Join-Path $_ 'platform-tools')) } | Select-Object -First 1 + if (-not $androidSdk) { + throw 'Could not locate the prepared Android SDK.' + } + New-Item -ItemType Directory -Force -Path '$(Build.StagingDirectory)/apk-tests-helix/results' | Out-Null + Write-Host "##vso[task.setvariable variable=ApkTestsAndroidSdkDirectory]$androidSdk" + displayName: Resolve APK test build paths + +- task: DownloadPipelineArtifact@2 + inputs: + artifactName: $(TestAssembliesArtifactName) + downloadPath: $(System.DefaultWorkingDirectory)/bin/Test$(XA.Build.Configuration) + +- task: DotNetCoreCLI@2 + displayName: Build BootstrapTasks Debug + inputs: + projects: $(System.DefaultWorkingDirectory)/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj + arguments: -c Debug -bl:$(Build.StagingDirectory)/apk-tests-helix/BootstrapTasks-Debug.binlog + +- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: Xamarin.Android.slnx + arguments: -t:PrepareJavaInterop -c Debug --no-restore + displayName: Prepare Java.Interop Debug + continueOnError: false + +- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: Xamarin.Android.slnx + arguments: -t:PrepareJavaInterop -c $(XA.Build.Configuration) --no-restore + displayName: Prepare Java.Interop $(XA.Build.Configuration) + continueOnError: false + +- ${{ each testCase in parameters.testCases }}: + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: build + project: ${{ testCase.project }} + arguments: >- + -t:Clean + -c ${{ testCase.configuration }} + -p:AndroidSdkDirectory="$(ApkTestsAndroidSdkDirectory)" + -p:JavaSdkDirectory="$(JAVA_HOME)" + ${{ testCase.extraBuildArguments }} + displayName: Clean APK payload - ${{ testCase.displayName }} + continueOnError: false + + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: build + project: ${{ testCase.project }} + arguments: >- + -t:SignAndroidPackage + -c ${{ testCase.configuration }} + -p:AndroidPackageFormat=apk + -p:AndroidSdkDirectory="$(ApkTestsAndroidSdkDirectory)" + -p:JavaSdkDirectory="$(JAVA_HOME)" + ${{ testCase.extraBuildArguments }} + -bl:$(Build.StagingDirectory)/apk-tests-helix/build-${{ testCase.name }}.binlog + displayName: Build APK payload - ${{ testCase.displayName }} + continueOnError: false + + - pwsh: | + $ErrorActionPreference = 'Stop' + $payloadDirectory = '$(Build.StagingDirectory)/apk-tests-helix/work-items/${{ testCase.name }}' + New-Item -ItemType Directory -Force -Path $payloadDirectory | Out-Null + $apk = Get-ChildItem '$(System.DefaultWorkingDirectory)/bin/Test${{ testCase.configuration }}' -Recurse -Filter '${{ testCase.artifactName }}' | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + if (-not $apk) { + throw "Could not find ${{ testCase.artifactName }} for ${{ testCase.displayName }}." + } + Copy-Item -LiteralPath $apk.FullName -Destination (Join-Path $payloadDirectory 'app.apk') + [pscustomobject]@{ + name = '${{ testCase.name }}' + displayName = '${{ testCase.displayName }}' + packageName = '${{ testCase.packageName }}' + instrumentation = '${{ testCase.instrumentation }}' + } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $payloadDirectory 'case.json') -Encoding utf8NoBOM + displayName: Stage APK payload - ${{ testCase.displayName }} + + - pwsh: | + $dotnet = '$(System.DefaultWorkingDirectory)\bin\$(XA.Build.Configuration)\dotnet\dotnet.exe' + & $dotnet build-server shutdown + exit $LASTEXITCODE + displayName: Shut down build servers - ${{ testCase.displayName }} + +- pwsh: | + $ErrorActionPreference = 'Stop' + $platformTools = Join-Path '$(ApkTestsAndroidSdkDirectory)' 'platform-tools' + if (-not (Test-Path -LiteralPath $platformTools -PathType Container)) { + throw 'Could not locate Android platform-tools.' + } + & '$(System.DefaultWorkingDirectory)/build-tools/automation/apk-tests-helix/prepare-apk-test-helix-submission.ps1' ` + -WorkItemsDirectory '$(Build.StagingDirectory)/apk-tests-helix/work-items' ` + -ItemsPropsPath '$(Build.StagingDirectory)/apk-tests-helix/work-items.props' ` + -ResultsDirectory '$(Build.StagingDirectory)/apk-tests-helix/results' ` + -PlatformToolsDirectory $platformTools ` + -TargetMinutes ${{ parameters.targetMinutes }} + displayName: Prepare APK test Helix submission + +- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: msbuild + project: $(System.DefaultWorkingDirectory)/build-tools/automation/ApkTestsHelix.proj + arguments: >- + /restore + /t:Test + /p:TreatWarningsAsErrors=false + /bl:$(Build.StagingDirectory)/apk-tests-helix/results/send-to-helix.binlog + displayName: Submit APK tests to Helix + continueOnError: false + taskTimeoutInMinutes: 180 + env: + ApkTestsHelixItemsProps: $(ApkTestsHelixItemsProps) + ApkTestsHelixWorkItemTimeout: ${{ parameters.workItemTimeout }} + HelixResultsDestinationDir: $(Build.StagingDirectory)/apk-tests-helix/results + HelixSource: pr/dotnet/android + HelixType: tests/android/apk-tests/ + HelixBuild: $(Build.BuildNumber) + HelixConfiguration: apk-tests + HelixTargetQueueForItem: ${{ parameters.helixQueue }} + HelixTestRunName: Android APK Tests - Helix Prototype + HelixAccessToken: '' + WaitForWorkItemCompletion: 'true' + Creator: dotnet-android + +- task: PublishTestResults@2 + displayName: Publish APK Helix TRX results + condition: always() + inputs: + testResultsFormat: VSTest + testResultsFiles: '**/*.trx' + searchFolder: $(Build.StagingDirectory)/apk-tests-helix/results + mergeTestResults: false + testRunTitle: Android APK Tests - Helix Prototype + +- task: PublishPipelineArtifact@1 + displayName: Publish APK Helix diagnostics + condition: always() + inputs: + artifactName: APK Test Helix Results + targetPath: $(Build.StagingDirectory)/apk-tests-helix/results