winget-cli

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

commit 2a9d1c66a32af0f44f6dd9b6ad76e88d427efc11
parent fc6c6a32f320ba099a41611db819a2ecea9718db
Author: Chacón <lechacon@users.noreply.github.com>
Date:   Tue, 14 Sep 2021 17:18:33 -0700

Add expected installer return codes (#1421)


Diffstat:
M.github/actions/spelling/allow.txt | 3+++
M.github/actions/spelling/expect.txt | 16+++++++++++++++-
Mschemas/JSON/manifests/v1.1.0/manifest.installer.1.1.0.json | 55+++++++++++++++++++++++++++++++++++++++++++++++++------
Mschemas/JSON/manifests/v1.1.0/manifest.singleton.1.1.0.json | 55+++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/AppInstallerCLICore/ExecutionContextData.h | 7+++++++
Msrc/AppInstallerCLICore/Resources.h | 20+++++++++++++++++++-
Msrc/AppInstallerCLICore/Workflows/InstallFlow.cpp | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/AppInstallerCLICore/Workflows/InstallFlow.h | 21+++++++++++++++++++++
Msrc/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp | 19++-----------------
Msrc/AppInstallerCLICore/Workflows/MsiInstallFlow.h | 2+-
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 19++-----------------
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h | 2+-
Msrc/AppInstallerCLIE2ETests/Constants.cs | 61++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 9+++++++++
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 1041++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/AppInstallerCLITests/RestInterface_1_1.cpp | 8+++++++-
Asrc/AppInstallerCLITests/TestData/InstallFlowTest_ExpectedReturnCodes.yaml | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml | 21+++++++++++++++++++++
Asrc/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml | 21+++++++++++++++++++++
Msrc/AppInstallerCLITests/TestData/ManifestV1_1-Singleton.yaml | 6++++++
Msrc/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Installer.yaml | 6++++++
Msrc/AppInstallerCLITests/WorkFlow.cpp | 22++++++++++++++++++++++
Msrc/AppInstallerCLITests/YamlManifest.cpp | 7+++++++
Msrc/AppInstallerCommonCore/Errors.cpp | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/Manifest/ManifestCommon.cpp | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp | 1+
Msrc/AppInstallerCommonCore/Manifest/ManifestValidation.cpp | 14++++++++++++++
Msrc/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/AppInstallerCommonCore/Public/AppInstallerErrors.h | 56+++++++++++++++++++++++++++++++++++++-------------------
Msrc/AppInstallerCommonCore/Public/winget/ManifestCommon.h | 31+++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/Public/winget/ManifestInstaller.h | 2++
Msrc/AppInstallerCommonCore/Public/winget/ManifestValidation.h | 1+
Msrc/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h | 4++++
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp | 24++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.cpp | 12++++++++++++
Msrc/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.h | 2++
37 files changed, 1364 insertions(+), 615 deletions(-)

diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -334,6 +334,7 @@ pdb PEVENT pfp PGP +php PII pipssource placeholders @@ -408,6 +409,7 @@ SERVICEPACKMAJOR SERVICEPACKMINOR setfill setschemaversion +setupexitcodes setvariable setw shcore @@ -536,6 +538,7 @@ URegular uri url urlmon +USEREXIT userguide USERPROFILE usersources diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -7,6 +7,7 @@ agg aicli AICLIC ajor +alreadyinstalled amrutha anonymized APARTMENTTHREADED @@ -20,8 +21,8 @@ argumentlist ARMNT arp arphelper -Ashwini ashpatil +Ashwini asm ASwitch Atest @@ -38,6 +39,7 @@ bght bitmask bkup blargle +blockedbypolicy blogs Bluetooth bomgar @@ -45,6 +47,7 @@ BOMs brk Buf BUILTINS +cancelledbyuser casemap casemappings cch @@ -67,6 +70,7 @@ COINIT COMGLB commandline Concat +contactsupport contosa contosainstaller contoso @@ -85,6 +89,7 @@ deigh deleteifnotneeded desktopappinstaller dirs +diskfull dnld dustojnikhummer dvinns @@ -105,6 +110,7 @@ experimentalfeatures fcb fd fedorapeople +fileinuse fintimes Fixfor flargle @@ -148,7 +154,9 @@ IMutable IName inet inor +installinprogress installshield +insufficientmemory IPackage IPersist IService @@ -201,6 +209,7 @@ middleware midl minexample minschema +missingdependency MMmmbbbb monicka MPNS @@ -223,6 +232,7 @@ mytool netlify Newtonsoft NOEXPAND +nonetwork normer NOSEPARATOR NOTAPROPERTY @@ -234,6 +244,7 @@ NX objbase ofile Packagedx +packageinuse pathparts pathpaths Patil @@ -262,6 +273,9 @@ qb qword rbegin readonly +rebootinitiated +rebootrequiredforinstall +rebootrequiredtofinish redirector regexes REGSAM diff --git a/schemas/JSON/manifests/v1.1.0/manifest.installer.1.1.0.json b/schemas/JSON/manifests/v1.1.0/manifest.installer.1.1.0.json @@ -132,20 +132,57 @@ } } }, + "InstallerReturnCode": { + "type": "integer", + "not": { + "enum": [ 0 ] + }, + "minimum": -2147483648, + "maximum": 429496725, + "description": "An exit code that can be returned by the installer after execution" + }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { - "type": "integer", - "not": { - "enum": [ 0 ] - }, - "minimum": -2147483648, - "maximum": 4294967295 + "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, + "ExpectedReturnCodes": { + "type": [ "array", "null" ], + "items": { + "type": "object", + "properties": { + "InstallerReturnCode": { + "$ref": "#/definitions/InstallerReturnCode" + }, + "ReturnResponse": { + "type": "string", + "enum": [ + "packageInUse", + "installInProgress", + "fileInUse", + "missingDependency", + "diskFull", + "insufficientMemory", + "noNetwork", + "contactSupport", + "rebootRequiredToFinish", + "rebootRequiredForInstall", + "rebootInitiated", + "cancelledByUser", + "alreadyInstalled", + "downgrade", + "blockedByPolicy" + ] + } + } + }, + "maxItems": 128, + "description": "Installer exit codes for common errors" + }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ @@ -449,6 +486,9 @@ "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, + "ExpectedReturnCodes": { + "$ref": "#/definitions/ExpectedReturnCodes" + }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, @@ -543,6 +583,9 @@ "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, + "ExpectedReturnCodes": { + "$ref": "#/definitions/ExpectedReturnCodes" + }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, diff --git a/schemas/JSON/manifests/v1.1.0/manifest.singleton.1.1.0.json b/schemas/JSON/manifests/v1.1.0/manifest.singleton.1.1.0.json @@ -165,20 +165,57 @@ } } }, + "InstallerReturnCode": { + "type": "integer", + "not": { + "enum": [ 0 ] + }, + "minimum": -2147483648, + "maximum": 4294967295, + "description": "An exit code that can be returned by the installer after execution" + }, "InstallerSuccessCodes": { "type": [ "array", "null" ], "items": { - "type": "integer", - "not": { - "enum": [ 0 ] - }, - "minimum": -2147483648, - "maximum": 4294967295 + "$ref": "#/definitions/InstallerReturnCode" }, "maxItems": 16, "uniqueItems": true, "description": "List of additional non-zero installer success exit codes other than known default values by winget" }, + "ExpectedReturnCodes": { + "type": [ "array", "null" ], + "items": { + "type": "object", + "properties": { + "InstallerReturnCode": { + "$ref": "#/definitions/InstallerReturnCode" + }, + "ReturnResponse": { + "type": "string", + "enum": [ + "packageInUse", + "installInProgress", + "fileInUse", + "missingDependency", + "diskFull", + "insufficientMemory", + "noNetwork", + "contactSupport", + "rebootRequiredToFinish", + "rebootRequiredForInstall", + "rebootInitiated", + "cancelledByUser", + "alreadyInstalled", + "downgrade", + "blockedByPolicy" + ] + } + } + }, + "maxItems": 128, + "description": "Installer exit codes for common errors" + }, "UpgradeBehavior": { "type": [ "string", "null" ], "enum": [ @@ -481,6 +518,9 @@ "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, + "ExpectedReturnCodes": { + "$ref": "#/definitions/ExpectedReturnCodes" + }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, @@ -677,6 +717,9 @@ "InstallerSuccessCodes": { "$ref": "#/definitions/InstallerSuccessCodes" }, + "ExpectedReturnCodes": { + "$ref": "#/definitions/ExpectedReturnCodes" + }, "UpgradeBehavior": { "$ref": "#/definitions/UpgradeBehavior" }, diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h @@ -34,6 +34,7 @@ namespace AppInstaller::CLI::Execution InstallerPath, LogPath, InstallerArgs, + InstallerReturnCode, CompletionData, InstalledPackageVersion, UninstallString, @@ -153,6 +154,12 @@ namespace AppInstaller::CLI::Execution }; template <> + struct DataMapping<Data::InstallerReturnCode> + { + using value_t = DWORD; + }; + + template <> struct DataMapping<Data::CompletionData> { using value_t = CLI::CompletionData; diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h @@ -91,25 +91,43 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(ImportPackageAlreadyInstalled); WINGET_DEFINE_RESOURCE_STRINGID(ImportSearchFailed); WINGET_DEFINE_RESOURCE_STRINGID(ImportSourceNotInstalled); + WINGET_DEFINE_RESOURCE_STRINGID(InstallAndUpgradeCommandsReportDependencies); + WINGET_DEFINE_RESOURCE_STRINGID(InstallationAbandoned); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer1); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer2); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimerMSStore); WINGET_DEFINE_RESOURCE_STRINGID(InstallationRequiresHigherWindows); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandLongDescription); - WINGET_DEFINE_RESOURCE_STRINGID(InstallAndUpgradeCommandsReportDependencies); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageNotAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageVersionNotAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstallerBlockedByPolicy); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedSecurityCheck); WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedVirusScan); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedWithCode); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchAdminBlock); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchError); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverridden); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverrideRequired); WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashVerified); + WINGET_DEFINE_RESOURCE_STRINGID(InstallerLogAvailable); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowInstallSuccess); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowRegistrationDeferred); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeAlreadyInstalled); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeBlockedByPolicy); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeCancelledByUser); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeContactSupport); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeDiskFull); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeDowngrade); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeFileInUse); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeInstallInProgress); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeInsufficientMemory); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeMissingDependency); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeNoNetwork); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodePackageInUse); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootInitiated); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootRequiredForInstall); + WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeRebootRequiredToFinish); WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowStartingPackageInstall); WINGET_DEFINE_RESOURCE_STRINGID(InstallForceArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallScopeDescription); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -54,6 +54,55 @@ namespace AppInstaller::CLI::Workflow return false; } } + + struct ExpectedReturnCode + { + ExpectedReturnCode(ExpectedReturnCodeEnum installerReturnCode, HRESULT hr, Resource::StringId message) : + InstallerReturnCode(installerReturnCode), HResult(hr), Message(message) {} + + static ExpectedReturnCode GetExpectedReturnCode(ExpectedReturnCodeEnum returnCode) + { + switch (returnCode) + { + case ExpectedReturnCodeEnum::PackageInUse: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE, Resource::String::InstallFlowReturnCodePackageInUse); + case ExpectedReturnCodeEnum::InstallInProgress: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS, Resource::String::InstallFlowReturnCodeInstallInProgress); + case ExpectedReturnCodeEnum::FileInUse: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE, Resource::String::InstallFlowReturnCodeFileInUse); + case ExpectedReturnCodeEnum::MissingDependency: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY, Resource::String::InstallFlowReturnCodeMissingDependency); + case ExpectedReturnCodeEnum::DiskFull: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL, Resource::String::InstallFlowReturnCodeDiskFull); + case ExpectedReturnCodeEnum::InsufficientMemory: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY, Resource::String::InstallFlowReturnCodeInsufficientMemory); + case ExpectedReturnCodeEnum::NoNetwork: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK, Resource::String::InstallFlowReturnCodeNoNetwork); + case ExpectedReturnCodeEnum::ContactSupport: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT, Resource::String::InstallFlowReturnCodeContactSupport); + case ExpectedReturnCodeEnum::RebootRequiredToFinish: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH, Resource::String::InstallFlowReturnCodeRebootRequiredToFinish); + case ExpectedReturnCodeEnum::RebootRequiredForInstall: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL, Resource::String::InstallFlowReturnCodeRebootRequiredForInstall); + case ExpectedReturnCodeEnum::RebootInitiated: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED, Resource::String::InstallFlowReturnCodeRebootInitiated); + case ExpectedReturnCodeEnum::CancelledByUser: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER, Resource::String::InstallFlowReturnCodeCancelledByUser); + case ExpectedReturnCodeEnum::AlreadyInstalled: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED, Resource::String::InstallFlowReturnCodeAlreadyInstalled); + case ExpectedReturnCodeEnum::Downgrade: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE, Resource::String::InstallFlowReturnCodeDowngrade); + case ExpectedReturnCodeEnum::BlockedByPolicy: + return ExpectedReturnCode(returnCode, APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY, Resource::String::InstallFlowReturnCodeBlockedByPolicy); + default: + THROW_HR(E_UNEXPECTED); + } + } + + ExpectedReturnCodeEnum InstallerReturnCode; + HRESULT HResult; + Resource::StringId Message; + }; } void EnsureApplicableInstaller(Execution::Context& context) @@ -432,7 +481,8 @@ namespace AppInstaller::CLI::Workflow context << GetInstallerArgs << RenameDownloadedInstaller << - ShellExecuteInstallImpl; + ShellExecuteInstallImpl << + ReportInstallerResult("ShellExecute"sv, APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED); } void DirectMSIInstall(Execution::Context& context) @@ -440,7 +490,8 @@ namespace AppInstaller::CLI::Workflow context << GetInstallerArgs << RenameDownloadedInstaller << - DirectMSIInstallImpl; + DirectMSIInstallImpl << + ReportInstallerResult("MsiInstallProduct"sv, APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED); } void MsixInstall(Execution::Context& context) @@ -468,11 +519,9 @@ namespace AppInstaller::CLI::Workflow } catch (const wil::ResultException& re) { - const auto& manifest = context.Get<Execution::Data::Manifest>(); - Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "MSIX", re.GetErrorCode()); - - context.Reporter.Error() << GetUserPresentableMessage(re) << std::endl; - AICLI_TERMINATE_CONTEXT(re.GetErrorCode()); + context.Add<Execution::Data::InstallerReturnCode>(re.GetErrorCode()); + context << ReportInstallerResult("MSIX"sv, re.GetErrorCode(), /* isHResult */ true); + return; } if (registrationDeferred) @@ -485,6 +534,48 @@ namespace AppInstaller::CLI::Workflow } } + void ReportInstallerResult::operator()(Execution::Context& context) const + { + DWORD installResult = context.Get<Execution::Data::InstallerReturnCode>(); + const auto& additionalSuccessCodes = context.Get<Execution::Data::Installer>()->InstallerSuccessCodes; + if (installResult != 0 && (std::find(additionalSuccessCodes.begin(), additionalSuccessCodes.end(), installResult) == additionalSuccessCodes.end())) + { + const auto& manifest = context.Get<Execution::Data::Manifest>(); + Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, m_installerType, installResult); + + if (m_isHResult) + { + context.Reporter.Error() << Resource::String::InstallerFailedWithCode << ' ' << GetUserPresentableMessage(installResult) << std::endl; + } + else + { + context.Reporter.Error() << Resource::String::InstallerFailedWithCode << ' ' << installResult << std::endl; + } + + // Show installer log path if exists + if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get<Execution::Data::LogPath>())) + { + context.Reporter.Info() << Resource::String::InstallerLogAvailable << ' ' << context.Get<Execution::Data::LogPath>().u8string() << std::endl; + } + + // Show a specific message if we can identify the return code + const auto& expectedReturnCodes = context.Get<Execution::Data::Installer>()->ExpectedReturnCodes; + auto expectedReturnCodeItr = expectedReturnCodes.find(installResult); + if (expectedReturnCodeItr != expectedReturnCodes.end() && expectedReturnCodeItr->second != ExpectedReturnCodeEnum::Unknown) + { + auto returnCode = ExpectedReturnCode::GetExpectedReturnCode(expectedReturnCodeItr->second); + context.Reporter.Error() << returnCode.Message << std::endl; + AICLI_TERMINATE_CONTEXT(returnCode.HResult); + } + + AICLI_TERMINATE_CONTEXT(m_hr); + } + else + { + context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + } + } + void RemoveInstaller(Execution::Context& context) { // Path may not be present if installed from a URL for MSIX diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -114,6 +114,27 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void MsixInstall(Execution::Context& context); + // Reports the return code returned by the installer. + // Required Args: None + // Inputs: Manifest, Installer, InstallerResult + // Outputs: None + struct ReportInstallerResult : public WorkflowTask + { + ReportInstallerResult(std::string_view installerType, HRESULT hr, bool isHResult = false) : + WorkflowTask("ReportInstallerResult"), m_installerType(installerType), m_hr(hr), m_isHResult(isHResult) {} + + void operator()(Execution::Context& context) const override; + + private: + // Installer type used when reporting failures. + std::string_view m_installerType; + // Result to return if the installer failed. + HRESULT m_hr; + // Whether the installer result is an HRESULT. This guides how we show it. + bool m_isHResult; + }; + + // Deletes the installer file. // Required Args: None // Inputs: InstallerPath diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp @@ -33,7 +33,6 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; const std::filesystem::path& installerPath = context.Get<Execution::Data::InstallerPath>(); - const auto& additionalSuccessCodes = context.Get<Execution::Data::Installer>()->InstallerSuccessCodes; Msi::MsiParsedArguments parsedArgs = Msi::ParseMSIArguments(context.Get<Execution::Data::InstallerArgs>()); @@ -45,26 +44,12 @@ namespace AppInstaller::CLI::Workflow if (!installResult) { - context.Reporter.Warn() << "Installation abandoned" << std::endl; + context.Reporter.Warn() << Resource::String::InstallationAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } - else if (installResult.value() != 0 && (std::find(additionalSuccessCodes.begin(), additionalSuccessCodes.end(), installResult.value()) == additionalSuccessCodes.end())) - { - const auto& manifest = context.Get<Execution::Data::Manifest>(); - Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "ShellExecute", installResult.value()); - - context.Reporter.Error() << "Installer failed with exit code: " << installResult.value() << std::endl; - // Show installer log path if exists - if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get<Execution::Data::LogPath>())) - { - context.Reporter.Info() << "Installer log is available at: " << context.Get<Execution::Data::LogPath>().u8string() << std::endl; - } - - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED); - } else { - context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + context.Add<Execution::Data::InstallerReturnCode>(installResult.value()); } } } diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.h @@ -8,6 +8,6 @@ namespace AppInstaller::CLI::Workflow // Ensures that there is an applicable installer. // Required Args: None // Inputs: InstallerArgs, Installer, InstallerPath, Manifest - // Outputs: None + // Outputs: InstallerReturnCode void DirectMSIInstallImpl(Execution::Context& context); } diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -188,7 +188,6 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl; const std::string& installerArgs = context.Get<Execution::Data::InstallerArgs>(); - const auto& additionalSuccessCodes = context.Get<Execution::Data::Installer>()->InstallerSuccessCodes; auto installResult = context.Reporter.ExecuteWithProgress( std::bind(InvokeShellExecute, @@ -198,26 +197,12 @@ namespace AppInstaller::CLI::Workflow if (!installResult) { - context.Reporter.Warn() << "Installation abandoned" << std::endl; + context.Reporter.Warn() << Resource::String::InstallationAbandoned << std::endl; AICLI_TERMINATE_CONTEXT(E_ABORT); } - else if (installResult.value() != 0 && (std::find(additionalSuccessCodes.begin(), additionalSuccessCodes.end(), installResult.value()) == additionalSuccessCodes.end())) - { - const auto& manifest = context.Get<Execution::Data::Manifest>(); - Logging::Telemetry().LogInstallerFailure(manifest.Id, manifest.Version, manifest.Channel, "ShellExecute", installResult.value()); - - context.Reporter.Error() << "Installer failed with exit code: " << installResult.value() << std::endl; - // Show installer log path if exists - if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get<Execution::Data::LogPath>())) - { - context.Reporter.Info() << "Installer log is available at: " << context.Get<Execution::Data::LogPath>().u8string() << std::endl; - } - - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED); - } else { - context.Reporter.Info() << Resource::String::InstallFlowInstallSuccess << std::endl; + context.Add<Execution::Data::InstallerReturnCode>(installResult.value()); } } diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.h @@ -14,7 +14,7 @@ namespace AppInstaller::CLI::Workflow // Install is done through invoking ShellExecute on downloaded installer. // Required Args: None // Inputs: Manifest?, InstallerPath, InstallerArgs - // Outputs: None + // Outputs: InstallerReturnCode void ShellExecuteInstallImpl(Execution::Context& context); // Uninstall is done through invoking ShellExecute on uninstall string. diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs @@ -58,8 +58,8 @@ namespace AppInstallerCLIE2ETests public const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002); public const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003); public const int ERROR_NO_RANGES_PROCESSED = unchecked((int)0x80070138); - public const int OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY = unchecked((int)0x8051100f); - public const int ERROR_OLD_WIN_VERSION = unchecked((int)0x8007047e); + public const int OPC_E_ZIP_MISSING_END_OF_CENTRAL_DIRECTORY = unchecked((int)0x8051100F); + public const int ERROR_OLD_WIN_VERSION = unchecked((int)0x8007047E); public const int HTTP_E_STATUS_NOT_FOUND = unchecked((int)0x80190194); // AICLI custom HRESULTs @@ -109,31 +109,50 @@ namespace AppInstallerCLIE2ETests public const int ERROR_UPDATE_ALL_HAS_FAILURE = unchecked((int)0x8A15002C); public const int ERROR_INSTALLER_SECURITY_CHECK_FAILED = unchecked((int)0x8A15002D); public const int ERROR_DOWNLOAD_SIZE_MISMATCH = unchecked((int)0x8A15002E); - public const int ERROR_NO_UNINSTALL_INFO_FOUND = unchecked((int)0x8a15002F); - public const int ERROR_EXEC_UNINSTALL_COMMAND_FAILED = unchecked((int)0x8a150030); + public const int ERROR_NO_UNINSTALL_INFO_FOUND = unchecked((int)0x8A15002F); + public const int ERROR_EXEC_UNINSTALL_COMMAND_FAILED = unchecked((int)0x8A150030); public const int ERROR_ICU_BREAK_ITERATOR_ERROR = unchecked((int)0x8A150031); public const int ERROR_ICU_CASEMAP_ERROR = unchecked((int)0x8A150032); public const int ERROR_ICU_REGEX_ERROR = unchecked((int)0x8A150033); - public const int ERROR_IMPORT_INSTALL_FAILED = unchecked((int)0x8a150034); - public const int ERROR_NOT_ALL_PACKAGES_FOUND = unchecked((int)0x8a150035); - public const int ERROR_JSON_INVALID_FILE = unchecked((int)0x8a150036); + public const int ERROR_IMPORT_INSTALL_FAILED = unchecked((int)0x8A150034); + public const int ERROR_NOT_ALL_PACKAGES_FOUND = unchecked((int)0x8A150035); + public const int ERROR_JSON_INVALID_FILE = unchecked((int)0x8A150036); public const int ERROR_SOURCE_NOT_REMOTE = unchecked((int)0x8A150037); public const int ERROR_UNSUPPORTED_RESTSOURCE = unchecked((int)0x8A150038); public const int ERROR_RESTSOURCE_INVALID_DATA = unchecked((int)0x8A150039); - public const int ERROR_BLOCKED_BY_POLICY = unchecked((int)0x8a15003A); - public const int ERROR_RESTSOURCE_INTERNAL_ERROR = unchecked((int)0x8a15003B); - public const int ERROR_RESTSOURCE_INVALID_URL = unchecked((int)0x8a15003C); - public const int ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE = unchecked((int)0x8a15003D); - public const int ERROR_RESTSOURCE_INVALID_VERSION = unchecked((int)0x8a15003E); - public const int ERROR_SOURCE_DATA_INTEGRITY_FAILURE = unchecked((int)0x8a15003F); - public const int ERROR_STREAM_READ_FAILURE = unchecked((int)0x8a150040); - public const int ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8a150041); - public const int ERROR_PROMPT_INPUT_ERROR = unchecked((int)0x8a150042); - public const int ERROR_UNSUPPORTED_SOURCE_REQUEST = unchecked((int)0x8a150043); - public const int ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND = unchecked((int)0x8a150044); - public const int ERROR_SOURCE_OPEN_FAILED = unchecked((int)0x8a150045); - public const int ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8a150046); - public const int ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH = unchecked((int)0x8a150047); + public const int ERROR_BLOCKED_BY_POLICY = unchecked((int)0x8A15003A); + public const int ERROR_RESTSOURCE_INTERNAL_ERROR = unchecked((int)0x8A15003B); + public const int ERROR_RESTSOURCE_INVALID_URL = unchecked((int)0x8A15003C); + public const int ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE = unchecked((int)0x8A15003D); + public const int ERROR_RESTSOURCE_INVALID_VERSION = unchecked((int)0x8A15003E); + public const int ERROR_SOURCE_DATA_INTEGRITY_FAILURE = unchecked((int)0x8A15003F); + public const int ERROR_STREAM_READ_FAILURE = unchecked((int)0x8A150040); + public const int ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8A150041); + public const int ERROR_PROMPT_INPUT_ERROR = unchecked((int)0x8A150042); + public const int ERROR_UNSUPPORTED_SOURCE_REQUEST = unchecked((int)0x8A150043); + public const int ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND = unchecked((int)0x8A150044); + public const int ERROR_SOURCE_OPEN_FAILED = unchecked((int)0x8A150045); + public const int ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED = unchecked((int)0x8A150046); + public const int ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH = unchecked((int)0x8A150047); + public const int ERROR_MISSING_RESOURCE_FILE = unchecked((int)0x8A150048); + public const int ERROR_MSI_INSTALL_FAILED = unchecked((int)0x8A150049); + public const int ERROR_INVALID_MSIEXEC_ARGUMENT = unchecked((int)0x8A15004A); + + public const int ERROR_INSTALL_PACKAGE_IN_USE = unchecked((int)0x8A150101); + public const int ERROR_INSTALL_INSTALL_IN_PROGRESS = unchecked((int)0x8A150102); + public const int ERROR_INSTALL_FILE_IN_USE = unchecked((int)0x8A150103); + public const int ERROR_INSTALL_MISSING_DEPENDENCY = unchecked((int)0x8A150104); + public const int ERROR_INSTALL_DISK_FULL = unchecked((int)0x8A150105); + public const int ERROR_INSTALL_INSUFFICIENT_MEMORY = unchecked((int)0x8A150106); + public const int ERROR_INSTALL_NO_NETWORK = unchecked((int)0x8A150107); + public const int ERROR_INSTALL_CONTACT_SUPPORT = unchecked((int)0x8A150108); + public const int ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH = unchecked((int)0x8A150109); + public const int ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL = unchecked((int)0x8A15010A); + public const int ERROR_INSTALL_REBOOT_INITIATED = unchecked((int)0x8A15010B); + public const int ERROR_INSTALL_CANCELLED_BY_USER = unchecked((int)0x8A15010C); + public const int ERROR_INSTALL_ALREADY_INSTALLED = unchecked((int)0x8A15010D); + public const int ERROR_INSTALL_DOWNGRADE = unchecked((int)0x8A15010E); + public const int ERROR_INSTALL_BLOCKED_BY_POLICY = unchecked((int)0x8A15010F); } } } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1097,4 +1097,58 @@ Do you agree to the terms?</value> <data name="AdminSettingDisabled" xml:space="preserve"> <value>Admin setting disabled.</value> </data> + <data name="InstallFlowReturnCodePackageInUse" xml:space="preserve"> + <value>Application is currently running. Exit the application then try again.</value> + </data> + <data name="InstallFlowReturnCodeInstallInProgress" xml:space="preserve"> + <value>Another installation is already in progress. Try again later.</value> + </data> + <data name="InstallFlowReturnCodeFileInUse" xml:space="preserve"> + <value>One or more files are being used. Exit the application then try again.</value> + </data> + <data name="InstallFlowReturnCodeMissingDependency" xml:space="preserve"> + <value>This package has a dependency missing from your system.</value> + </data> + <data name="InstallFlowReturnCodeDiskFull" xml:space="preserve"> + <value>There's no more space on your PC. Make space, then try again.</value> + </data> + <data name="InstallFlowReturnCodeInsufficientMemory" xml:space="preserve"> + <value>There's not enough memory available to install. Close other applications then try again.</value> + </data> + <data name="InstallFlowReturnCodeNoNetwork" xml:space="preserve"> + <value>This application requires internet connectivity. Connect to a network then try again.</value> + </data> + <data name="InstallFlowReturnCodeContactSupport" xml:space="preserve"> + <value>This application encountered an error during installation. Contact support.</value> + </data> + <data name="InstallFlowReturnCodeRebootRequiredToFinish" xml:space="preserve"> + <value>Restart your PC to finish installation.</value> + </data> + <data name="InstallFlowReturnCodeRebootRequiredForInstall" xml:space="preserve"> + <value>Your PC will restart to finish installation.</value> + </data> + <data name="InstallFlowReturnCodeRebootInitiated" xml:space="preserve"> + <value>Installation failed. Restart your PC then try again.</value> + </data> + <data name="InstallFlowReturnCodeCancelledByUser" xml:space="preserve"> + <value>You cancelled the installation.</value> + </data> + <data name="InstallFlowReturnCodeAlreadyInstalled" xml:space="preserve"> + <value>Another version of this application is already installed.</value> + </data> + <data name="InstallFlowReturnCodeDowngrade" xml:space="preserve"> + <value>A higher version of this application is already installed.</value> + </data> + <data name="InstallFlowReturnCodeBlockedByPolicy" xml:space="preserve"> + <value>Organization policies are preventing installation. Contact your admin.</value> + </data> + <data name="InstallAbandoned" xml:space="preserve"> + <value>Installation abandoned</value> + </data> + <data name="InstallerFailedWithCode" xml:space="preserve"> + <value>Installer failed with exit code:</value> + </data> + <data name="InstallerLogAvailable" xml:space="preserve"> + <value>Installer log is available at:</value> + </data> </root> \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -259,6 +259,9 @@ <CopyFileToFolders Include="TestData\InstallFlowTest_LicenseAgreement.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_ExpectedReturnCodes.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\ImportFile-Bad-Invalid.json"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> @@ -337,6 +340,12 @@ <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -1,517 +1,526 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - <Filter Include="TestData"> - <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> - </Filter> - <Filter Include="TestData\MultiFileManifestV1"> - <UniqueIdentifier>{69fcd25c-e737-4d28-a6d1-39ce491bf293}</UniqueIdentifier> - </Filter> - <Filter Include="TestData\MultiFileManifestV1_1"> - <UniqueIdentifier>{81fadc81-4327-4b9e-b588-97155b770aa3}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="pch.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestCommon.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestHooks.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestSource.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestSettings.h"> - <Filter>Header Files</Filter> - </ClInclude> - <ClInclude Include="TestRestRequestHandler.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="pch.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="main.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteWrapper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteIndex.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestCommon.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="YamlManifest.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Downloader.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="LanguageUtilities.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Settings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Sources.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="WorkFlow.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Synchronization.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="MsixInfo.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PreIndexedPackageSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteIndexSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="HashCommand.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Versions.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Strings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Command.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="UserSettings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ExperimentalFeature.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Completion.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PredefinedInstalledSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="CompositeSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestSource.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Registry.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="NameNormalization.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PackageCollection.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="Regex.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ARPChanges.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="GroupPolicy.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="WorkflowGroupPolicy.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestSettings.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="ManifestComparator.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="JsonHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestClient.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestInterface_1_0.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="TestRestRequestHandler.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="HttpClientHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SearchRequestSerializer.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="CustomHeader.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="MsiExecArguments.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="RestInterface_1_1.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="PropertySheet.props" /> - <None Include="packages.config" /> - <None Include="Run-TestsInPackage.ps1" /> - </ItemGroup> - <ItemGroup> - <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidLocale.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-LicenseMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DefaultLang.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffLangs.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-MultiLocale.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\TestSignedApp.msix"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NonZeroExitCode.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_MSStore.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-ANSI.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE-BOM.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-SystemReferenceComplex.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-PackageFamilyNameOnMSI.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-ProductCodeOnMSIX.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidUpdateBehavior.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix_LicenseAgreement.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-AlreadyInstalled.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-WithLicenseAgreement.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-Malformed.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackage.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackageVersion.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownSource.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Bad-Invalid.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InputNames.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InputPublishers.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\NormalizationInitialIds.txt"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ManifestV1-Singleton.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ManifestV1_1-Singleton.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-DefaultLocale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Installer.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Locale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Version.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-DefaultLocale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Installer.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Locale.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Version.yaml"> - <Filter>TestData\MultiFileManifestV1</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-MachineScope.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Manifest-Good-AllDependencyTypes.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\ImportFile-Good-Dependencies.json"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_Dependencies.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\UpdateFlowTest_ExeDependencies.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Msix_WFDependency.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesOnRoot.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesMultideclaration.yaml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - </ItemGroup> +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="TestData"> + <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> + </Filter> + <Filter Include="TestData\MultiFileManifestV1"> + <UniqueIdentifier>{69fcd25c-e737-4d28-a6d1-39ce491bf293}</UniqueIdentifier> + </Filter> + <Filter Include="TestData\MultiFileManifestV1_1"> + <UniqueIdentifier>{81fadc81-4327-4b9e-b588-97155b770aa3}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestCommon.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestHooks.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestSource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestSettings.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestRestRequestHandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteWrapper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteIndex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestCommon.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="YamlManifest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Downloader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="LanguageUtilities.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Settings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Sources.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WorkFlow.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Synchronization.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MsixInfo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PreIndexedPackageSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteIndexSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HashCommand.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Versions.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Strings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Command.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="UserSettings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ExperimentalFeature.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Completion.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PredefinedInstalledSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CompositeSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestSource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Registry.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="NameNormalization.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PackageCollection.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Regex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ARPChanges.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="GroupPolicy.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WorkflowGroupPolicy.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestSettings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ManifestComparator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JsonHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestClient.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestInterface_1_0.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestRestRequestHandler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HttpClientHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SearchRequestSerializer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="CustomHeader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MsiExecArguments.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="RestInterface_1_1.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <None Include="PropertySheet.props" /> + <None Include="packages.config" /> + <None Include="Run-TestsInPackage.ps1" /> + </ItemGroup> + <ItemGroup> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidLocale.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidManifestVersionValue.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-LicenseMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnsupportedVersion.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DefaultLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffLangs.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-MultiLocale.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\index.2.0.0.0.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\TestSignedApp.msix"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_NonZeroExitCode.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_MSStore.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_ExpectedReturnCodes.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Spaces.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-ANSI.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF8-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16BE-BOM.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Encoding-UTF16LE.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-SystemReferenceComplex.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PackageFamilyNameOnMSI.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-ProductCodeOnMSIX.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InvalidUpdateBehavior.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Msix_LicenseAgreement.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-AlreadyInstalled.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-WithLicenseAgreement.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-Malformed.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackage.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownPackageVersion.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-UnknownSource.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Bad-Invalid.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InputNames.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InputPublishers.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\NormalizationInitialIds.txt"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ManifestV1-Singleton.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ManifestV1_1-Singleton.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-DefaultLocale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Installer.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Locale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1\ManifestV1-MultiFile-Version.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-DefaultLocale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Installer.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Locale.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\MultiFileManifestV1_1\ManifestV1_1-MultiFile-Version.yaml"> + <Filter>TestData\MultiFileManifestV1</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_Exe_2.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-MachineScope.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-AllDependencyTypes.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\ImportFile-Good-Dependencies.json"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_Dependencies.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\UpdateFlowTest_ExeDependencies.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Msix_WFDependency.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesOnRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Installer_Exe_DependenciesMultideclaration.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AppInstallerCLITests/RestInterface_1_1.cpp b/src/AppInstallerCLITests/RestInterface_1_1.cpp @@ -176,7 +176,11 @@ namespace }], "Markets" : { "AllowedMarkets": [ "US" ] - } + }, + "ExpectedReturnCodes": [{ + "InstallerReturnCode": 3, + "ReturnResponse": "InstallInProgress" + }] } ] } @@ -293,6 +297,8 @@ namespace REQUIRE(actualInstaller.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(actualInstaller.Markets.AllowedMarkets.size() == 1); REQUIRE(actualInstaller.Markets.AllowedMarkets.at(0) == "US"); + REQUIRE(actualInstaller.ExpectedReturnCodes.size() == 1); + REQUIRE(actualInstaller.ExpectedReturnCodes.at(3) == ExpectedReturnCodeEnum::InstallInProgress); } }; } diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_ExpectedReturnCodes.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_ExpectedReturnCodes.yaml @@ -0,0 +1,46 @@ +PackageIdentifier: AppInstallerCliTest.TestInstaller +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +ShortDescription: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +Installers: + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + ExpectedReturnCodes: + - InstallerReturnCode: 1 + ReturnResponse: packageInUse + - InstallerReturnCode: 2 + ReturnResponse: installInProgress + - InstallerReturnCode: 3 + ReturnResponse: fileInUse + - InstallerReturnCode: 4 + ReturnResponse: missingDependency + - InstallerReturnCode: 5 + ReturnResponse: diskFull + - InstallerReturnCode: 6 + ReturnResponse: insufficientMemory + - InstallerReturnCode: 7 + ReturnResponse: noNetwork + - InstallerReturnCode: 8 + ReturnResponse: contactSupport + - InstallerReturnCode: 9 + ReturnResponse: rebootRequiredToFinish + - InstallerReturnCode: 10 + ReturnResponse: rebootRequiredForInstall + - InstallerReturnCode: 11 + ReturnResponse: rebootInitiated + - InstallerReturnCode: 12 + ReturnResponse: cancelledByUser + - InstallerReturnCode: 13 + ReturnResponse: alreadyInstalled + - InstallerReturnCode: 14 + ReturnResponse: downgrade + - InstallerReturnCode: 15 + ReturnResponse: blockedByPolicy +ManifestType: singleton +ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml @@ -0,0 +1,21 @@ +# Bad manifest. Expected return codes repeat the same return code +PackageIdentifier: AppInstallerCliTest.TestInstaller +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +ShortDescription: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +Installers: + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + ExpectedReturnCodes: + - InstallerReturnCode: 1 + ReturnResponse: PackageInUse + - InstallerReturnCode: 1 + ReturnResponse: InstallInProgress +ManifestType: singleton +ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml @@ -0,0 +1,21 @@ +# Bad manifest. Expected return codes repeat the same return code +PackageIdentifier: AppInstallerCliTest.TestInstaller +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +ShortDescription: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +Installers: + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B + InstallerSuccessCodes: + - 1 + ExpectedReturnCodes: + - InstallerReturnCode: 1 + ReturnResponse: PackageInUse +ManifestType: singleton +ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/TestData/ManifestV1_1-Singleton.yaml b/src/AppInstallerCLITests/TestData/ManifestV1_1-Singleton.yaml @@ -91,6 +91,9 @@ AppsAndFeaturesEntries: Markets: AllowedMarkets: - US +ExpectedReturnCodes: + - InstallerReturnCode: 10 + ReturnResponse: packageInUse Installers: - Architecture: x86 @@ -149,6 +152,9 @@ Installers: Markets: ExcludedMarkets: - "US" + ExpectedReturnCodes: + - InstallerReturnCode: 2 + ReturnResponse: contactSupport ManifestType: singleton ManifestVersion: 1.1.0 diff --git a/src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Installer.yaml b/src/AppInstallerCLITests/TestData/MultiFileManifestV1_1/ManifestV1_1-MultiFile-Installer.yaml @@ -67,6 +67,9 @@ AppsAndFeaturesEntries: Markets: AllowedMarkets: - "US" +ExpectedReturnCodes: + - InstallerReturnCode: 10 + ReturnResponse: packageInUse Installers: - Architecture: x86 @@ -125,6 +128,9 @@ Installers: Markets: ExcludedMarkets: - "US" + ExpectedReturnCodes: + - InstallerReturnCode: 2 + ReturnResponse: contactSupport - Architecture: x64 InstallerType: exe InstallerUrl: https://www.microsoft.com/msixsdk/msixsdkx64.exe diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -436,6 +436,8 @@ void OverrideForDirectMsi(TestContext& context) std::ofstream file(temp, std::ofstream::out); file << context.Get<Execution::Data::InstallerArgs>(); file.close(); + + context.Add<Execution::Data::InstallerReturnCode>(0); } }); } @@ -588,6 +590,26 @@ TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]") REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } +TEST_CASE("InstallFlow_ExpectedReturnCodes", "[InstallFlow][workflow]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_ExpectedReturnCodes.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Override, "/ExitCode 8"sv); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // Verify install failed with the right message + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT); + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::InstallFlowReturnCodeContactSupport).get()) != std::string::npos); +} + TEST_CASE("InstallFlowWithNonApplicableArchitecture", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp @@ -223,6 +223,8 @@ TEST_CASE("ReadBadManifests", "[ManifestValidation]") { "Manifest-Bad-DuplicateKey.yaml", "Duplicate field found in the manifest." }, { "Manifest-Bad-DuplicateKey-DifferentCase.yaml", "Duplicate field found in the manifest." }, { "Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml", "Duplicate field found in the manifest." }, + { "Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml", "Duplicate installer return code found." }, + { "Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml", "Duplicate installer return code found." }, { "Manifest-Bad-IdInvalid.yaml", "Failed to validate against schema associated with property name 'Id'" }, { "Manifest-Bad-IdMissing.yaml", "Missing required property 'Id'" }, { "Manifest-Bad-InstallersMissing.yaml", "Missing required property 'Installers'" }, @@ -423,6 +425,8 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.size() == 1); REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.at(0) == "US"); + REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.size() == 1); + REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10) == ExpectedReturnCodeEnum::PackageInUse); } if (isSingleton) @@ -484,6 +488,7 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer1.Markets.AllowedMarkets.size() == 0); REQUIRE(installer1.Markets.ExcludedMarkets.size() == 1); REQUIRE(installer1.Markets.ExcludedMarkets.at(0) == "US"); + REQUIRE(installer1.ExpectedReturnCodes.at(2) == ExpectedReturnCodeEnum::ContactSupport); } if (!isSingleton) @@ -513,6 +518,8 @@ void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, Manifes REQUIRE(installer2.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); REQUIRE(installer2.Markets.AllowedMarkets.size() == 1); REQUIRE(installer2.Markets.AllowedMarkets.at(0) == "US"); + REQUIRE(installer2.ExpectedReturnCodes.size() == 1); + REQUIRE(installer2.ExpectedReturnCodes.at(10) == ExpectedReturnCodeEnum::PackageInUse); } // Localization diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp @@ -146,6 +146,8 @@ namespace AppInstaller return "Package agreements were not agreed to"; case APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR: return "Error reading input in prompt"; + case APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT: + return "Arguments for msiexec are invalid"; case APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST: return "The search request is not supported by one or more sources"; case APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND: @@ -156,6 +158,38 @@ namespace AppInstaller return "Source agreements were not agreed to"; case APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH: return "Header size exceeds the allowable limit of 1024 characters. Please reduce the size and try again."; + case APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED: + return "Running MSI install failed"; + case APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE: + return "Application is currently running.Exit the application then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS: + return "Another installation is already in progress.Try again later."; + case APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE: + return "One or more file is being used.Exit the application then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY: + return "This package has a dependency missing from your system."; + case APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL: + return "There's no more space on your PC. Make space, then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY: + return "There's not enough memory available to install. Close other applications then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK: + return "This application requires internet connectivity.Connect to a network then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT: + return "This application encountered an error during installation.Contact support."; + case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH: + return "Restart your PC to finish installation."; + case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL: + return "Your PC will restart to finish installation."; + case APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED: + return "Installation failed. Restart your PC then try again."; + case APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER: + return "You cancelled the installation."; + case APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED: + return "Another version of this application is already installed."; + case APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE: + return "A higher version of this application is already installed."; + case APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY: + return "Organization policies are preventing installation. Contact your admin."; default: return "Unknown Error Code"; } @@ -198,6 +232,13 @@ namespace AppInstaller return e.what(); } + std::string GetUserPresentableMessage(HRESULT hr) + { + std::ostringstream strstr; + GetUserPresentableMessageForHR(strstr, hr); + return strstr.str(); + } + #ifndef WINGET_DISABLE_FOR_FUZZING std::string GetUserPresentableMessage(const winrt::hresult_error& hre) { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp @@ -282,6 +282,75 @@ namespace AppInstaller::Manifest } } + ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in) + { + std::string inStrLower = Utility::ToLower(in); + ExpectedReturnCodeEnum result = ExpectedReturnCodeEnum::Unknown; + + if (inStrLower == "packageinuse") + { + result = ExpectedReturnCodeEnum::PackageInUse; + } + else if (inStrLower == "installinprogress") + { + result = ExpectedReturnCodeEnum::InstallInProgress; + } + else if (inStrLower == "fileinuse") + { + result = ExpectedReturnCodeEnum::FileInUse; + } + else if (inStrLower == "missingdependency") + { + result = ExpectedReturnCodeEnum::MissingDependency; + } + else if (inStrLower == "diskfull") + { + result = ExpectedReturnCodeEnum::DiskFull; + } + else if (inStrLower == "insufficientmemory") + { + result = ExpectedReturnCodeEnum::InsufficientMemory; + } + else if (inStrLower == "nonetwork") + { + result = ExpectedReturnCodeEnum::NoNetwork; + } + else if (inStrLower == "contactsupport") + { + result = ExpectedReturnCodeEnum::ContactSupport; + } + else if (inStrLower == "rebootrequiredtofinish") + { + result = ExpectedReturnCodeEnum::RebootRequiredToFinish; + } + else if (inStrLower == "rebootrequiredforinstall") + { + result = ExpectedReturnCodeEnum::RebootRequiredForInstall; + } + else if (inStrLower == "rebootinitiated") + { + result = ExpectedReturnCodeEnum::RebootInitiated; + } + else if (inStrLower == "cancelledbyuser") + { + result = ExpectedReturnCodeEnum::CancelledByUser; + } + else if (inStrLower == "alreadyinstalled") + { + result = ExpectedReturnCodeEnum::AlreadyInstalled; + } + else if (inStrLower == "downgrade") + { + result = ExpectedReturnCodeEnum::Downgrade; + } + else if (inStrLower == "blockedbypolicy") + { + result = ExpectedReturnCodeEnum::BlockedByPolicy; + } + + return result; + } + std::string_view InstallerTypeToString(InstallerTypeEnum installerType) { switch (installerType) @@ -410,4 +479,48 @@ namespace AppInstaller::Manifest return {}; } } + + std::map<DWORD, ExpectedReturnCodeEnum> GetDefaultKnownReturnCodes(InstallerTypeEnum installerType) + { + switch (installerType) + { + case InstallerTypeEnum::Burn: + case InstallerTypeEnum::Wix: + case InstallerTypeEnum::Msi: + // See https://docs.microsoft.com/windows/win32/msi/error-codes + return + { + { ERROR_INSTALL_ALREADY_RUNNING, ExpectedReturnCodeEnum::InstallInProgress }, + { ERROR_DISK_FULL, ExpectedReturnCodeEnum::DiskFull }, + { ERROR_INSTALL_SERVICE_FAILURE, ExpectedReturnCodeEnum::ContactSupport }, + { ERROR_SUCCESS_REBOOT_REQUIRED, ExpectedReturnCodeEnum::RebootRequiredToFinish }, + { ERROR_SUCCESS_REBOOT_INITIATED, ExpectedReturnCodeEnum::RebootInitiated }, + { ERROR_INSTALL_USEREXIT, ExpectedReturnCodeEnum::CancelledByUser }, + { ERROR_PRODUCT_VERSION, ExpectedReturnCodeEnum::AlreadyInstalled }, + { ERROR_INSTALL_REJECTED, ExpectedReturnCodeEnum::BlockedByPolicy }, + }; + case InstallerTypeEnum::Inno: + // See https://jrsoftware.org/ishelp/index.php?topic=setupexitcodes + return + { + { 2, ExpectedReturnCodeEnum::CancelledByUser }, + { 5, ExpectedReturnCodeEnum::CancelledByUser }, + { 8, ExpectedReturnCodeEnum::RebootRequiredForInstall }, + }; + case InstallerTypeEnum::Msix: + // See https://docs.microsoft.com/en-us/windows/win32/appxpkg/troubleshooting + return + { + { HRESULT_FROM_WIN32(ERROR_INSTALL_PREREQUISITE_FAILED), ExpectedReturnCodeEnum::MissingDependency }, + { HRESULT_FROM_WIN32(ERROR_INSTALL_RESOLVE_DEPENDENCY_FAILED), ExpectedReturnCodeEnum::MissingDependency }, + { HRESULT_FROM_WIN32(ERROR_INSTALL_OPTIONAL_PACKAGE_REQUIRES_MAIN_PACKAGE), ExpectedReturnCodeEnum::MissingDependency }, + { HRESULT_FROM_WIN32(ERROR_INSTALL_OUT_OF_DISK_SPACE), ExpectedReturnCodeEnum::DiskFull }, + { HRESULT_FROM_WIN32(ERROR_INSTALL_CANCEL), ExpectedReturnCodeEnum::CancelledByUser }, + { HRESULT_FROM_WIN32(ERROR_PACKAGE_ALREADY_EXISTS), ExpectedReturnCodeEnum::AlreadyInstalled }, + { HRESULT_FROM_WIN32(ERROR_INSTALL_PACKAGE_DOWNGRADE), ExpectedReturnCodeEnum::Downgrade }, + }; + default: + return {}; + } + } } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestSchemaValidation.cpp @@ -29,6 +29,7 @@ namespace AppInstaller::Manifest::YamlParser { "InstallerAbortsTerminal"sv, YamlScalarType::Bool }, { "InstallLocationRequired"sv, YamlScalarType::Bool }, { "RequireExplicitUpgrade"sv, YamlScalarType::Bool }, + { "InstallerReturnCode"sv, YamlScalarType::Int }, }; YamlScalarType GetManifestScalarValueType(const std::string& key) diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -162,6 +162,20 @@ namespace AppInstaller::Manifest { resultErrors.emplace_back(ManifestError::BothAllowedAndExcludedMarketsDefined); } + + // Check expected return codes for duplicates between successful and expected error codes + std::set<DWORD> returnCodeSet; + returnCodeSet.insert(installer.InstallerSuccessCodes.begin(), installer.InstallerSuccessCodes.end()); + for (const auto& code : installer.ExpectedReturnCodes) + { + if (!returnCodeSet.insert(code.first).second) + { + resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); + + // Stop checking to avoid repeated errors + break; + } + } } // Validate localizations diff --git a/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp b/src/AppInstallerCommonCore/Manifest/ManifestYamlPopulator.cpp @@ -276,6 +276,7 @@ namespace AppInstaller::Manifest { "ElevationRequirement", [this](const YAML::Node& value)->ValidationErrors { m_p_installer->ElevationRequirement = ConvertToElevationRequirementEnum(value.as<std::string>()); return {}; } }, { "Markets", [this](const YAML::Node& value)->ValidationErrors { return ProcessMarketsNode(value); } }, { "AppsAndFeaturesEntries", [this](const YAML::Node& value)->ValidationErrors { return ProcessAppsAndFeaturesEntriesNode(value); } }, + { "ExpectedReturnCodes", [this](const YAML::Node& value)->ValidationErrors { return ProcessExpectedReturnCodesNode(value); } }, }; std::move(fields_v1_1.begin(), fields_v1_1.end(), std::inserter(result, result.end())); @@ -313,6 +314,19 @@ namespace AppInstaller::Manifest return result; } + std::vector<ManifestYamlPopulator::FieldProcessInfo> ManifestYamlPopulator::GetExpectedReturnCodesFieldProcessInfo(const ManifestVer& manifestVersion) + { + std::vector<FieldProcessInfo> result = {}; + + if (manifestVersion >= ManifestVer{ s_ManifestVersionV1_1 }) + { + result.emplace_back("InstallerReturnCode", [this](const YAML::Node& value)->ValidationErrors { m_p_expectedReturnCode->InstallerReturnCode = static_cast<int>(value.as<int>()); return {}; }); + result.emplace_back("ReturnResponse", [this](const YAML::Node& value)->ValidationErrors { m_p_expectedReturnCode->ReturnResponse = ConvertToExpectedReturnCodeEnum(value.as<std::string>()); return {}; }); + } + + return result; + } + std::vector<ManifestYamlPopulator::FieldProcessInfo> ManifestYamlPopulator::GetLocalizationFieldProcessInfo(const ManifestVer& manifestVersion, bool forRootFields) { // Common fields across versions @@ -571,7 +585,7 @@ namespace AppInstaller::Manifest return resultErrors; } - std::vector<ValidationError> ManifestYamlPopulator::ProcessAgreementsNode(const YAML::Node& agreementsNode) + ValidationErrors ManifestYamlPopulator::ProcessAgreementsNode(const YAML::Node& agreementsNode) { THROW_HR_IF(E_INVALIDARG, !agreementsNode.IsSequence()); @@ -625,6 +639,31 @@ namespace AppInstaller::Manifest return resultErrors; } + ValidationErrors ManifestYamlPopulator::ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode) + { + THROW_HR_IF(E_INVALIDARG, !returnCodesNode.IsSequence()); + + ValidationErrors resultErrors; + std::map<DWORD, ExpectedReturnCodeEnum> returnCodes; + + for (auto const& entry : returnCodesNode.Sequence()) + { + ExpectedReturnCode returnCode; + m_p_expectedReturnCode = &returnCode; + auto errors = ValidateAndProcessFields(entry, ExpectedReturnCodesFieldInfos); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); + + if (!returnCodes.insert({ returnCode.InstallerReturnCode, returnCode.ReturnResponse }).second) + { + resultErrors.emplace_back(ManifestError::DuplicateReturnCodeEntry); + } + } + + m_p_installer->ExpectedReturnCodes = returnCodes; + + return resultErrors; + } + ValidationErrors ManifestYamlPopulator::PopulateManifestInternal(const YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, bool fullValidation) { m_fullValidation = fullValidation; @@ -637,6 +676,7 @@ namespace AppInstaller::Manifest RootFieldInfos = GetRootFieldProcessInfo(manifestVersion); InstallerFieldInfos = GetInstallerFieldProcessInfo(manifestVersion); SwitchesFieldInfos = GetSwitchesFieldProcessInfo(manifestVersion); + ExpectedReturnCodesFieldInfos = GetExpectedReturnCodesFieldProcessInfo(manifestVersion); DependenciesFieldInfos = GetDependenciesFieldProcessInfo(manifestVersion); PackageDependenciesFieldInfos = GetPackageDependenciesFieldProcessInfo(manifestVersion); LocalizationFieldInfos = GetLocalizationFieldProcessInfo(manifestVersion); @@ -703,6 +743,16 @@ namespace AppInstaller::Manifest } } + // Populate installer default return codes if not present + auto defaultReturnCodes = GetDefaultKnownReturnCodes(installer.InstallerType); + for (auto const& defaultReturnCode : defaultReturnCodes) + { + if (installer.ExpectedReturnCodes.find(defaultReturnCode.first) == installer.ExpectedReturnCodes.end()) + { + installer.ExpectedReturnCodes[defaultReturnCode.first] = defaultReturnCode.second; + } + } + manifest.Installers.emplace_back(std::move(installer)); } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -64,28 +64,45 @@ #define APPINSTALLER_CLI_ERROR_ICU_BREAK_ITERATOR_ERROR ((HRESULT)0x8A150031) #define APPINSTALLER_CLI_ERROR_ICU_CASEMAP_ERROR ((HRESULT)0x8A150032) #define APPINSTALLER_CLI_ERROR_ICU_REGEX_ERROR ((HRESULT)0x8A150033) -#define APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED ((HRESULT)0x8a150034) -#define APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND ((HRESULT)0x8a150035) -#define APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE ((HRESULT)0x8a150036) +#define APPINSTALLER_CLI_ERROR_IMPORT_INSTALL_FAILED ((HRESULT)0x8A150034) +#define APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND ((HRESULT)0x8A150035) +#define APPINSTALLER_CLI_ERROR_JSON_INVALID_FILE ((HRESULT)0x8A150036) #define APPINSTALLER_CLI_ERROR_SOURCE_NOT_REMOTE ((HRESULT)0x8A150037) #define APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE ((HRESULT)0x8A150038) #define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_DATA ((HRESULT)0x8A150039) -#define APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY ((HRESULT)0x8a15003A) -#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR ((HRESULT)0x8a15003B) -#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL ((HRESULT)0x8a15003C) -#define APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE ((HRESULT)0x8a15003D) -#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION ((HRESULT)0x8a15003E) -#define APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE ((HRESULT)0x8a15003F) -#define APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE ((HRESULT)0x8a150040) -#define APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8a150041) -#define APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR ((HRESULT)0x8a150042) -#define APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT ((HRESULT)0x8a150043) -#define APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST ((HRESULT)0x8a150043) -#define APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND ((HRESULT)0x8a150044) -#define APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED ((HRESULT)0x8a150045) -#define APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8a150046) -#define APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH ((HRESULT)0x8a150047) -#define APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE ((HRESULT)0x8a150048) +#define APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY ((HRESULT)0x8A15003A) +#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR ((HRESULT)0x8A15003B) +#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL ((HRESULT)0x8A15003C) +#define APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE ((HRESULT)0x8A15003D) +#define APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_VERSION ((HRESULT)0x8A15003E) +#define APPINSTALLER_CLI_ERROR_SOURCE_DATA_INTEGRITY_FAILURE ((HRESULT)0x8A15003F) +#define APPINSTALLER_CLI_ERROR_STREAM_READ_FAILURE ((HRESULT)0x8A150040) +#define APPINSTALLER_CLI_ERROR_PACKAGE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8A150041) +#define APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR ((HRESULT)0x8A150042) +#define APPINSTALLER_CLI_ERROR_UNSUPPORTED_SOURCE_REQUEST ((HRESULT)0x8A150043) +#define APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND ((HRESULT)0x8A150044) +#define APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED ((HRESULT)0x8A150045) +#define APPINSTALLER_CLI_ERROR_SOURCE_AGREEMENTS_NOT_ACCEPTED ((HRESULT)0x8A150046) +#define APPINSTALLER_CLI_ERROR_CUSTOMHEADER_EXCEEDS_MAXLENGTH ((HRESULT)0x8A150047) +#define APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE ((HRESULT)0x8A150048) +#define APPINSTALLER_CLI_ERROR_MSI_INSTALL_FAILED ((HRESULT)0x8A150049) +#define APPINSTALLER_CLI_ERROR_INVALID_MSIEXEC_ARGUMENT ((HRESULT)0x8A15004A) + +#define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101) +#define APPINSTALLER_CLI_ERROR_INSTALL_INSTALL_IN_PROGRESS ((HRESULT)0x8A150102) +#define APPINSTALLER_CLI_ERROR_INSTALL_FILE_IN_USE ((HRESULT)0x8A150103) +#define APPINSTALLER_CLI_ERROR_INSTALL_MISSING_DEPENDENCY ((HRESULT)0x8A150104) +#define APPINSTALLER_CLI_ERROR_INSTALL_DISK_FULL ((HRESULT)0x8A150105) +#define APPINSTALLER_CLI_ERROR_INSTALL_INSUFFICIENT_MEMORY ((HRESULT)0x8A150106) +#define APPINSTALLER_CLI_ERROR_INSTALL_NO_NETWORK ((HRESULT)0x8A150107) +#define APPINSTALLER_CLI_ERROR_INSTALL_CONTACT_SUPPORT ((HRESULT)0x8A150108) +#define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_FINISH ((HRESULT)0x8A150109) +#define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_REQUIRED_TO_INSTALL ((HRESULT)0x8A15010A) +#define APPINSTALLER_CLI_ERROR_INSTALL_REBOOT_INITIATED ((HRESULT)0x8A15010B) +#define APPINSTALLER_CLI_ERROR_INSTALL_CANCELLED_BY_USER ((HRESULT)0x8A15010C) +#define APPINSTALLER_CLI_ERROR_INSTALL_ALREADY_INSTALLED ((HRESULT)0x8A15010D) +#define APPINSTALLER_CLI_ERROR_INSTALL_DOWNGRADE ((HRESULT)0x8A15010E) +#define APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY ((HRESULT)0x8A15010F) namespace AppInstaller @@ -93,6 +110,7 @@ namespace AppInstaller // Gets error messages that are presentable to the user. std::string GetUserPresentableMessage(const wil::ResultException& re); std::string GetUserPresentableMessage(const std::exception& e); + std::string GetUserPresentableMessage(HRESULT hr); #ifndef WINGET_DISABLE_FOR_FUZZING std::string GetUserPresentableMessage(const winrt::hresult_error& hre); diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -97,6 +97,32 @@ namespace AppInstaller::Manifest SilentWithProgress, }; + enum class ExpectedReturnCodeEnum + { + Unknown, + PackageInUse, + InstallInProgress, + FileInUse, + MissingDependency, + DiskFull, + InsufficientMemory, + NoNetwork, + ContactSupport, + RebootRequiredToFinish, + RebootRequiredForInstall, + RebootInitiated, + CancelledByUser, + AlreadyInstalled, + Downgrade, + BlockedByPolicy, + }; + + struct ExpectedReturnCode + { + DWORD InstallerReturnCode; + ExpectedReturnCodeEnum ReturnResponse; + }; + enum class PlatformEnum { Unknown, @@ -273,6 +299,8 @@ namespace AppInstaller::Manifest ManifestTypeEnum ConvertToManifestTypeEnum(const std::string& in); + ExpectedReturnCodeEnum ConvertToExpectedReturnCodeEnum(const std::string& in); + std::string_view InstallerTypeToString(InstallerTypeEnum installerType); std::string_view ScopeToString(ScopeEnum scope); @@ -291,4 +319,7 @@ namespace AppInstaller::Manifest // Get a list of default switches for known installer types std::map<InstallerSwitchType, Utility::NormalizedString> GetDefaultKnownSwitches(InstallerTypeEnum installerType); + + // Get a list of default return codes for known installer types + std::map<DWORD, ExpectedReturnCodeEnum> GetDefaultKnownReturnCodes(InstallerTypeEnum installerType); } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h b/src/AppInstallerCommonCore/Public/winget/ManifestInstaller.h @@ -51,6 +51,8 @@ namespace AppInstaller::Manifest std::vector<DWORD> InstallerSuccessCodes; + std::map<DWORD, ExpectedReturnCodeEnum> ExpectedReturnCodes; + UpdateBehaviorEnum UpdateBehavior = UpdateBehaviorEnum::Install; std::vector<string_t> Commands; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h b/src/AppInstallerCommonCore/Public/winget/ManifestValidation.h @@ -41,6 +41,7 @@ namespace AppInstaller::Manifest const char* const FieldFailedToProcess = "Failed to process field."; const char* const InvalidBcp47Value = "The locale value is not a well formed bcp47 language tag."; const char* const BothAllowedAndExcludedMarketsDefined = "Both AllowedMarkets and ExcludedMarkets defined."; + const char* const DuplicateReturnCodeEntry = "Duplicate installer return code found."; } struct ValidationError diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h b/src/AppInstallerCommonCore/Public/winget/ManifestYamlPopulator.h @@ -29,6 +29,7 @@ namespace AppInstaller::Manifest std::vector<FieldProcessInfo> RootFieldInfos; std::vector<FieldProcessInfo> InstallerFieldInfos; std::vector<FieldProcessInfo> SwitchesFieldInfos; + std::vector<FieldProcessInfo> ExpectedReturnCodesFieldInfos; std::vector<FieldProcessInfo> DependenciesFieldInfos; std::vector<FieldProcessInfo> PackageDependenciesFieldInfos; std::vector<FieldProcessInfo> LocalizationFieldInfos; @@ -40,6 +41,7 @@ namespace AppInstaller::Manifest AppInstaller::Manifest::Manifest* m_p_manifest = nullptr; AppInstaller::Manifest::ManifestInstaller* m_p_installer = nullptr; std::map<InstallerSwitchType, Utility::NormalizedString>* m_p_switches = nullptr; + AppInstaller::Manifest::ExpectedReturnCode* m_p_expectedReturnCode = nullptr; AppInstaller::Manifest::DependencyList* m_p_dependencyList = nullptr; AppInstaller::Manifest::Dependency* m_p_packageDependency = nullptr; AppInstaller::Manifest::ManifestLocalization* m_p_localization = nullptr; @@ -54,6 +56,7 @@ namespace AppInstaller::Manifest std::vector<FieldProcessInfo> GetRootFieldProcessInfo(const ManifestVer& manifestVersion); std::vector<FieldProcessInfo> GetInstallerFieldProcessInfo(const ManifestVer& manifestVersion, bool forRootFields = false); std::vector<FieldProcessInfo> GetSwitchesFieldProcessInfo(const ManifestVer& manifestVersion); + std::vector<FieldProcessInfo> GetExpectedReturnCodesFieldProcessInfo(const ManifestVer& manifestVersion); std::vector<FieldProcessInfo> GetDependenciesFieldProcessInfo(const ManifestVer& manifestVersion); std::vector<FieldProcessInfo> GetPackageDependenciesFieldProcessInfo(const ManifestVer& manifestVersion); std::vector<FieldProcessInfo> GetLocalizationFieldProcessInfo(const ManifestVer& manifestVersion, bool forRootFields = false); @@ -74,6 +77,7 @@ namespace AppInstaller::Manifest std::vector<ValidationError> ProcessAgreementsNode(const YAML::Node& agreementsNode); std::vector<ValidationError> ProcessMarketsNode(const YAML::Node& marketsNode); std::vector<ValidationError> ProcessAppsAndFeaturesEntriesNode(const YAML::Node& appsAndFeaturesEntriesNode); + std::vector<ValidationError> ProcessExpectedReturnCodesNode(const YAML::Node& returnCodesNode); std::vector<ValidationError> PopulateManifestInternal(const YAML::Node& rootNode, Manifest& manifest, const ManifestVer& manifestVersion, bool fullValidation); }; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Json/ManifestDeserializer_1_1.cpp @@ -28,6 +28,9 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json constexpr std::string_view AllowedMarkets = "AllowedMarkets"sv; constexpr std::string_view ExcludedMarkets = "ExcludedMarkets"sv; constexpr std::string_view ElevationRequirement = "ElevationRequirement"sv; + constexpr std::string_view ExpectedReturnCodes = "ExpectedReturnCodes"sv; + constexpr std::string_view InstallerReturnCode = "InstallerReturnCode"sv; + constexpr std::string_view ReturnResponse = "ReturnResponse"sv; // Locale constexpr std::string_view ReleaseNotes = "ReleaseNotes"sv; @@ -123,6 +126,27 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1::Json installer.Markets.AllowedMarkets = V1_0::Json::ManifestDeserializer::ConvertToManifestStringArray( JsonHelper::GetRawStringArrayFromJsonNode(marketsNode.value().get(), JsonHelper::GetUtilityString(AllowedMarkets))); } + + // Expected return codes + std::optional<std::reference_wrapper<const web::json::array>> expectedReturnCodesNode = JsonHelper::GetRawJsonArrayFromJsonNode(installerJsonObject, JsonHelper::GetUtilityString(ExpectedReturnCodes)); + if (expectedReturnCodesNode) + { + for (auto& returnCodeNode : expectedReturnCodesNode.value().get()) + { + ExpectedReturnCodeEnum returnResponse = Manifest::ConvertToExpectedReturnCodeEnum(JsonHelper::GetRawStringValueFromJsonNode(returnCodeNode, JsonHelper::GetUtilityString(ReturnResponse)).value_or("")); + DWORD installerReturnCode = static_cast<DWORD>(JsonHelper::GetRawIntValueFromJsonNode(returnCodeNode, JsonHelper::GetUtilityString(InstallerReturnCode)).value_or(0)); + + // Only add when it is valid + if (installerReturnCode != 0 && returnResponse != ExpectedReturnCodeEnum::Unknown) + { + if (!installer.ExpectedReturnCodes.insert({ installerReturnCode, returnResponse }).second) + { + AICLI_LOG(Repo, Error, << "Expected return codes cannot have repeated value."); + return {}; + } + } + } + } } return result; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.cpp @@ -52,6 +52,18 @@ namespace AppInstaller::Repository::Rest::Schema return value.as_integer(); } + std::optional<int> JsonHelper::GetRawIntValueFromJsonNode(const web::json::value& node, const utility::string_t& keyName) + { + std::optional<std::reference_wrapper<const web::json::value>> jsonValue = GetJsonValueFromNode(node, keyName); + + if (jsonValue) + { + return GetRawIntValueFromJsonValue(jsonValue.value().get()); + } + + return {}; + } + std::optional<bool> JsonHelper::GetRawBoolValueFromJsonValue(const web::json::value& value) { if (value.is_null() || !value.is_boolean()) diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.h b/src/AppInstallerRepositoryCore/Rest/Schema/JsonHelper.h @@ -23,6 +23,8 @@ namespace AppInstaller::Repository::Rest::Schema static std::optional<int> GetRawIntValueFromJsonValue(const web::json::value& value); + static std::optional<int> GetRawIntValueFromJsonNode(const web::json::value& value, const utility::string_t& keyName); + static utility::string_t GetUtilityString(std::string_view nodeName); static std::vector<std::string> GetRawStringArrayFromJsonNode(const web::json::value& node, const utility::string_t& keyName);