winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 8b475657fb80e1685eb2e1a1ac7e9a29e22bef7a
parent d94b94bfaff4b2262780a7c80a837fefe8d55870
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Wed, 18 Mar 2020 15:12:34 -0700

Run tests in packaged context (#59)


Diffstat:
Mazure-pipelines.yml | 50+++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 1+
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 1+
Asrc/AppInstallerCLITests/Run-TestsInPackage.ps1 | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/main.cpp | 16+++++++++++++++-
Msrc/AppInstallerCommonCore/Deployment.cpp | 3++-
6 files changed, 212 insertions(+), 7 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -19,12 +19,15 @@ variables: steps: - task: NuGetToolInstaller@1 + displayName: Install Nuget - task: NuGetCommand@2 + displayName: Restore Packages inputs: restoreSolution: '$(solution)' - task: VSBuild@1 + displayName: Build Solution inputs: platform: 'x86' solution: '$(solution)' @@ -32,26 +35,61 @@ steps: msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload' - task: CmdLine@2 + displayName: Run Tests Unpackaged inputs: script: | - AppInstallerCLITests.exe -logto AICLI.log -s -r junit -o TEST-AppInstallerCLI-$(_artifact).xml + AppInstallerCLITests.exe -logto AICLI-Unpackaged.log -s -r junit -o TEST-AppInstallerCLI-Unpackaged.xml workingDirectory: 'src\x64\Release\AppInstallerCLITests\' - task: PublishBuildArtifacts@1 + displayName: Publish Unpackaged Log inputs: - PathtoPublish: 'src\x64\Release\AppInstallerCLITests\AICLI.log' - ArtifactName: 'TestPassLog' + PathtoPublish: 'src\x64\Release\AppInstallerCLITests\AICLI-Unpackaged.log' + ArtifactName: 'TestPassUnpackagedLog' publishLocation: 'Container' condition: succeededOrFailed() - task: PublishBuildArtifacts@1 + displayName: Publish Unpackaged Output inputs: - PathtoPublish: 'src\x64\Release\AppInstallerCLITests\TEST-AppInstallerCLI-$(_artifact).xml' - ArtifactName: 'TestPassOutput' + PathtoPublish: 'src\x64\Release\AppInstallerCLITests\TEST-AppInstallerCLI-Unpackaged.xml' + ArtifactName: 'TestPassUnpackagedOutput' + publishLocation: 'Container' + condition: succeededOrFailed() + +- task: PowerShell@2 + displayName: Install Dependencies + inputs: + targetType: 'inline' + script: | + Add-AppxPackage AppInstallerCLIPackage_0.0.0.2_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx + workingDirectory: $(appxPackageDir) + +- task: PowerShell@2 + displayName: Run Tests Packaged + inputs: + filePath: 'src\AppInstallerCLITests\Run-TestsInPackage.ps1' + arguments: '-Args "~[pips]" -BuildRoot x64\Release -PackageRoot AppInstallerCLIPackage\bin\x64\Release -LogTarget x64\Release\AICLI-Packaged.log -TestResultsTarget x64\Release\TEST-AppInstallerCLI-Packaged.xml -ScriptWait' + workingDirectory: 'src' + +- task: PublishBuildArtifacts@1 + displayName: Publish Packaged Log + inputs: + PathtoPublish: 'src\x64\Release\AICLI-Packaged.log' + ArtifactName: 'TestPassPackagedLog' + publishLocation: 'Container' + condition: succeededOrFailed() + +- task: PublishBuildArtifacts@1 + displayName: Publish Packaged Output + inputs: + PathtoPublish: 'src\x64\Release\TEST-AppInstallerCLI-Packaged.xml' + ArtifactName: 'TestPassPackagedOutput' publishLocation: 'Container' condition: succeededOrFailed() - task: PublishTestResults@2 + displayName: Publish Test Results inputs: testResultsFormat: 'JUnit' testResultsFiles: '**/TEST-*.xml' @@ -59,12 +97,14 @@ steps: condition: succeededOrFailed() - task: PublishBuildArtifacts@1 + displayName: Publish CLI Binary inputs: PathtoPublish: 'src\x64\Release\AppInstallerCLI\AppInstallerCLI.exe' ArtifactName: 'AppInstallerCLI.exe' publishLocation: 'Container' - task: ComponentGovernanceComponentDetection@0 + displayName: Component Governance inputs: scanType: 'Register' verbosity: 'Verbose' diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -221,6 +221,7 @@ <CopyFileToFolders Include="TestData\TestSignedApp.msix"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <None Include="Run-TestsInPackage.ps1" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AppInstallerCLICore\AppInstallerCLICore.vcxproj"> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -87,6 +87,7 @@ <ItemGroup> <None Include="PropertySheet.props" /> <None Include="packages.config" /> + <None Include="Run-TestsInPackage.ps1" /> </ItemGroup> <ItemGroup> <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> diff --git a/src/AppInstallerCLITests/Run-TestsInPackage.ps1 b/src/AppInstallerCLITests/Run-TestsInPackage.ps1 @@ -0,0 +1,148 @@ +<# +.SYNOPSIS + Runs the AppInstallerCLI tests within the packaged context. +.DESCRIPTION + Registers the loose files generated by the AppInstallerCLIPackage project, then runs the + existing tests from "within" this context. +.PARAMETER BuildRoot + The root of the build output directory. If not provided, assumed to be the local default + location relative to this script. +.PARAMETER PackageRoot + The root of the package build output directory. If not provided, assumed to be the local default + location relative to this script. +.PARAMETER LogTarget + The file path to log to. +.PARAMETER TestResultsTarget + The file path to place the test result file in. +.PARAMETER Args + Additional args to pass to the tests. +.PARAMETER Wait + Have the test process wait for user input before exiting. +.PARAMETER ScriptWait + Have the script wait for the output files to be freed before exiting. +#> +param( + [Parameter(Mandatory=$false)] + [string]$BuildRoot, + + [Parameter(Mandatory=$false)] + [string]$PackageRoot, + + [Parameter(Mandatory=$false)] + [string]$LogTarget, + + [Parameter(Mandatory=$false)] + [string]$TestResultsTarget, + + [Parameter(Mandatory=$false)] + [string]$Args, + + [switch]$Wait, + + [switch]$ScriptWait +) + +function Wait-ForFileClose([string]$Path) +{ + $Local:FileInfo = [System.IO.FileInfo]::new($Path) + $Local:SleepCount = 0 + + while ($Local:SleepCount -lt 300) + { + try + { + [System.IO.FileStream] $Local:Stream = $Local:FileInfo.OpenWrite() + $Local:Stream.Dispose() + break + } + catch + { + Start-Sleep 1 + $Local:SleepCount = $Local:SleepCount + 1 + } + } +} + +if ([String]::IsNullOrEmpty($BuildRoot)) +{ + $BuildRoot = Split-Path -Parent $PSCommandPath; + $BuildRoot = Join-Path $BuildRoot "..\x64\Debug"; +} +$BuildRoot = Resolve-Path $BuildRoot +Write-Host "Using BuildRoot = $BuildRoot" + +if ([String]::IsNullOrEmpty($PackageRoot)) +{ + $PackageRoot = Split-Path -Parent $PSCommandPath; + $PackageRoot = Join-Path $PackageRoot "..\AppInstallerCLIPackage\bin\x64\Debug"; +} +$PackageRoot = Resolve-Path $PackageRoot +Write-Host "Using PackageRoot = $PackageRoot" + +if (![String]::IsNullOrEmpty($LogTarget)) +{ + $Local:temp = Split-Path -Parent $LogTarget + $Local:temp = Resolve-Path $Local:temp + $LogTarget = Join-Path $Local:temp (Split-Path -Leaf $LogTarget) + Write-Host "Using LogTarget = $LogTarget" +} + +if (![String]::IsNullOrEmpty($TestResultsTarget)) +{ + $Local:temp = Split-Path -Parent $TestResultsTarget + $Local:temp = Resolve-Path $Local:temp + $TestResultsTarget = Join-Path $Local:temp (Split-Path -Leaf $TestResultsTarget) + Write-Host "Using TestResultsTarget = $TestResultsTarget" +} + +# Register the package; this requires the local package to have been deployed at least once or it won't be built. +$Local:ManifestPath = Join-Path $PackageRoot "AppxManifest.xml" +if (-not (Test-Path $Local:ManifestPath)) +{ + $Local:ManifestPath = Join-Path $PackageRoot "AppX\AppxManifest.xml" +} +Write-Host "Registering manifest at path: $Local:ManifestPath" +Add-AppxPackage -Register $Local:ManifestPath + +# Execute the tests from within the package's runtime. +$Local:TestExePath = Join-Path $BuildRoot "AppInstallerCLITests\AppInstallerCLITests.exe" +$Local:TestArgs = $Args + +if ([String]::IsNullOrEmpty($LogTarget)) +{ + $Local:TestArgs = $Local:TestArgs + " -log" +} +else +{ + $Local:TestArgs = $Local:TestArgs + " -logto ""$LogTarget""" +} + +if (![String]::IsNullOrEmpty($TestResultsTarget)) +{ + $Local:TestArgs = $Local:TestArgs + " -s -r junit -o ""$TestResultsTarget""" +} + +if ($Wait) +{ + $Local:TestArgs = $Local:TestArgs + " -wait" +} + +Write-Host "Executing tests at path: $Local:TestExePath" +Write-Host "Executing tests with args: $Local:TestArgs" +Invoke-CommandInDesktopPackage -PackageFamilyName AppInstallerCLI_8wekyb3d8bbwe -AppId AppInst -Command $Local:TestExePath -Args $Local:TestArgs + +if ($ScriptWait) +{ + Write-Host "Waiting for output files to be closed..." + Start-Sleep 5 + if (![String]::IsNullOrEmpty($LogTarget)) + { + Wait-ForFileClose $LogTarget + } + + if (![String]::IsNullOrEmpty($TestResultsTarget)) + { + Wait-ForFileClose $TestResultsTarget + } + Write-Host "Done" +} diff --git a/src/AppInstallerCLITests/main.cpp b/src/AppInstallerCLITests/main.cpp @@ -3,6 +3,7 @@ #define CATCH_CONFIG_RUNNER #include <catch.hpp> #include <winrt/Windows.Foundation.h> +#include <iostream> #include <string> #include <vector> @@ -49,6 +50,7 @@ int main(int argc, char** argv) init_apartment(); bool hasSetTestDataBasePath = false; + bool waitBeforeReturn = false; std::vector<char*> args; for (int i = 0; i < argc; ++i) @@ -79,6 +81,10 @@ int main(int argc, char** argv) hasSetTestDataBasePath = true; } } + else if ("-wait"s == argv[i]) + { + waitBeforeReturn = true; + } else { args.push_back(argv[i]); @@ -108,5 +114,13 @@ int main(int argc, char** argv) // This prevents test runs from trashing the users actual settings. AppInstaller::Runtime::TestHook_ForceContainerPrepend("AutoTestContainer"); - return Catch::Session().run(static_cast<int>(args.size()), args.data()); + int result = Catch::Session().run(static_cast<int>(args.size()), args.data()); + + if (waitBeforeReturn) + { + // Wait for some input before returning + std::cin.get(); + } + + return result; } diff --git a/src/AppInstallerCommonCore/Deployment.cpp b/src/AppInstallerCommonCore/Deployment.cpp @@ -56,7 +56,8 @@ namespace AppInstaller::Deployment { AICLI_LOG(Core, Error, << "Deployment failed #" << id << ": " << Utility::ConvertToUTF8(deployResult.ErrorText())); - THROW_HR_MSG(deployResult.ExtendedErrorCode(), "Install failed: %s", Utility::ConvertToUTF8(deployResult.ErrorText()).c_str()); + // Note that while the format string is char*, it gets converted to wchar before being used and thus %s needs a wchar. + THROW_HR_MSG(deployResult.ExtendedErrorCode(), "Install failed: %s", deployResult.ErrorText().c_str()); } else {