commit a14de79013927fab44f529fd2299a99414d3b059
parent 467adfa34ede4cc7291022a28eef3b9a5d8c69d9
Author: Ruben Guerrero <rubengu@microsoft.com>
Date: Tue, 9 Nov 2021 15:56:17 -0800
Print message when install technology is different in upgrade scenarios (#1649)
Diffstat:
10 files changed, 255 insertions(+), 71 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
@@ -221,6 +221,7 @@ IIS
ILogger
IManifest
impl
+inapplicabilities
Inet
inheritdoc
inno
diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h
@@ -338,6 +338,8 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(UpdateNotApplicable);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(UpgradeCommandShortDescription);
+ WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnology);
+ WINGET_DEFINE_RESOURCE_STRINGID(UpgradeDifferentInstallTechnologyInNewerVersions);
WINGET_DEFINE_RESOURCE_STRINGID(Usage);
WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandLongDescription);
WINGET_DEFINE_RESOURCE_STRINGID(ValidateCommandReportDependencies);
diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp
@@ -74,8 +74,6 @@ namespace AppInstaller::CLI::Workflow
return DependencyNodeProcessorResult::Error;
}
- std::optional<AppInstaller::Manifest::ManifestInstaller> installer;
-
IPackageVersion::Metadata installationMetadata;
if (m_nodePackageInstalledVersion)
{
@@ -83,7 +81,7 @@ namespace AppInstaller::CLI::Workflow
}
ManifestComparator manifestComparator(m_context, installationMetadata);
- installer = manifestComparator.GetPreferredInstaller(m_nodeManifest);
+ auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(m_nodeManifest);
if (!installer.has_value())
{
diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp
@@ -26,9 +26,14 @@ namespace AppInstaller::CLI::Workflow
{
OSVersionFilter() : details::FilterField("OS Version") {}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
- return installer.MinOSVersion.empty() || Runtime::IsCurrentOSVersionGreaterThanOrEqual(Utility::Version(installer.MinOSVersion));
+ if (installer.MinOSVersion.empty() || Runtime::IsCurrentOSVersionGreaterThanOrEqual(Utility::Version(installer.MinOSVersion)))
+ {
+ return InapplicabilityFlags::None;
+ }
+
+ return InapplicabilityFlags::OSVersion;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -97,9 +102,14 @@ namespace AppInstaller::CLI::Workflow
return std::make_unique<MachineArchitectureComparator>();
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
- return CheckAllowedArchitecture(installer.Arch) != Utility::InapplicableArchitecture;
+ if (CheckAllowedArchitecture(installer.Arch) == Utility::InapplicableArchitecture)
+ {
+ return InapplicabilityFlags::MachineArchitecture;
+ }
+
+ return InapplicabilityFlags::None;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -182,9 +192,14 @@ namespace AppInstaller::CLI::Workflow
return {};
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
- return Manifest::IsInstallerTypeCompatible(installer.InstallerType, m_installedType);
+ if (Manifest::IsInstallerTypeCompatible(installer.InstallerType, m_installedType))
+ {
+ return InapplicabilityFlags::None;
+ }
+
+ return InapplicabilityFlags::InstalledType;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -224,10 +239,15 @@ namespace AppInstaller::CLI::Workflow
return {};
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
// We have to assume the unknown scope will match our required scope, or the entire catalog would stop working for upgrade.
- return installer.Scope == Manifest::ScopeEnum::Unknown || installer.Scope == m_requirement;
+ if (installer.Scope == Manifest::ScopeEnum::Unknown || installer.Scope == m_requirement)
+ {
+ return InapplicabilityFlags::None;
+ }
+
+ return InapplicabilityFlags::InstalledScope;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -275,9 +295,14 @@ namespace AppInstaller::CLI::Workflow
}
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
- return m_requirement == Manifest::ScopeEnum::Unknown || installer.Scope == m_requirement;
+ if (m_requirement == Manifest::ScopeEnum::Unknown || installer.Scope == m_requirement)
+ {
+ return InapplicabilityFlags::None;
+ }
+
+ return InapplicabilityFlags::Scope;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -328,10 +353,16 @@ namespace AppInstaller::CLI::Workflow
return {};
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
// We have to assume an unknown installer locale will match our installed locale, or the entire catalog would stop working for upgrade.
- return installer.Locale.empty() || Locale::GetDistanceOfLanguage(m_installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch;
+ if (installer.Locale.empty() ||
+ Locale::GetDistanceOfLanguage(m_installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch)
+ {
+ return InapplicabilityFlags::None;
+ }
+
+ return InapplicabilityFlags::InstalledLocale;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -397,22 +428,22 @@ namespace AppInstaller::CLI::Workflow
}
}
- bool IsApplicable(const Manifest::ManifestInstaller& installer) override
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) override
{
if (m_requirement.empty())
{
- return true;
+ return InapplicabilityFlags::None;
}
for (auto const& requiredLocale : m_requirement)
{
if (Locale::GetDistanceOfLanguage(requiredLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsPerfectMatch)
{
- return true;
+ return InapplicabilityFlags::None;
}
}
- return false;
+ return InapplicabilityFlags::Locale;
}
std::string ExplainInapplicable(const Manifest::ManifestInstaller& installer) override
@@ -502,24 +533,33 @@ namespace AppInstaller::CLI::Workflow
AddComparator(MachineArchitectureComparator::Create(context, installationMetadata));
}
- std::optional<Manifest::ManifestInstaller> ManifestComparator::GetPreferredInstaller(const Manifest::Manifest& manifest)
+ InstallerAndInapplicabilities ManifestComparator::GetPreferredInstaller(const Manifest::Manifest& manifest)
{
AICLI_LOG(CLI, Info, << "Starting installer selection.");
const Manifest::ManifestInstaller* result = nullptr;
+ std::vector<InapplicabilityFlags> inapplicabilitiesInstallers;
for (const auto& installer : manifest.Installers)
{
- if (IsApplicable(installer) && (!result || IsFirstBetter(installer, *result)))
+ auto inapplicabilityInstaller = IsApplicable(installer);
+ if (inapplicabilityInstaller == InapplicabilityFlags::None)
{
- AICLI_LOG(CLI, Verbose, << "Installer " << installer << " is current best choice");
- result = &installer;
+ if (!result || IsFirstBetter(installer, *result))
+ {
+ AICLI_LOG(CLI, Verbose, << "Installer " << installer << " is current best choice");
+ result = &installer;
+ }
+ }
+ else
+ {
+ inapplicabilitiesInstallers.push_back(inapplicabilityInstaller);
}
}
if (!result)
{
- return {};
+ return { {}, std::move(inapplicabilitiesInstallers) };
}
Logging::Telemetry().LogSelectedInstaller(
@@ -529,22 +569,24 @@ namespace AppInstaller::CLI::Workflow
Manifest::ScopeToString(result->Scope),
result->Locale);
- return *result;
+ return { *result, std::move(inapplicabilitiesInstallers) };
}
- // TODO: Implement a mechanism for better error messaging for no applicable installer scenario
- bool ManifestComparator::IsApplicable(const Manifest::ManifestInstaller& installer)
+ InapplicabilityFlags ManifestComparator::IsApplicable(const Manifest::ManifestInstaller& installer)
{
+ InapplicabilityFlags inapplicabilityResult = InapplicabilityFlags::None;
+
for (const auto& filter : m_filters)
{
- if (!filter->IsApplicable(installer))
+ auto inapplicability = filter->IsApplicable(installer);
+ if (inapplicability != InapplicabilityFlags::None)
{
AICLI_LOG(CLI, Info, << "Installer " << installer << " not applicable: " << filter->ExplainInapplicable(installer));
- return false;
+ WI_SetAllFlags(inapplicabilityResult, inapplicability);
}
}
- return true;
+ return inapplicabilityResult;
}
bool ManifestComparator::IsFirstBetter(
diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.h b/src/AppInstallerCLICore/Workflows/ManifestComparator.h
@@ -13,6 +13,21 @@
namespace AppInstaller::CLI::Workflow
{
+ // Flags to indicate why an installer was not applicable
+ enum class InapplicabilityFlags : int
+ {
+ None = 0x0,
+ OSVersion = 0x1,
+ InstalledScope = 0x2,
+ InstalledType = 0x4,
+ InstalledLocale = 0x8,
+ Locale = 0x10,
+ Scope = 0x20,
+ MachineArchitecture = 0x40,
+ };
+
+ DEFINE_ENUM_FLAG_OPERATORS(InapplicabilityFlags);
+
namespace details
{
// An interface for defining new filters based on user inputs.
@@ -25,7 +40,7 @@ namespace AppInstaller::CLI::Workflow
std::string_view Name() const { return m_name; }
// Determines if the installer is applicable based on this field alone.
- virtual bool IsApplicable(const Manifest::ManifestInstaller& installer) = 0;
+ virtual InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer) = 0;
// Explains why the filter regarded this installer as inapplicable.
// Will only be called when IsApplicable returns false.
@@ -47,16 +62,22 @@ namespace AppInstaller::CLI::Workflow
};
}
+ struct InstallerAndInapplicabilities
+ {
+ std::optional<Manifest::ManifestInstaller> installer;
+ std::vector<InapplicabilityFlags> inapplicabilitiesInstaller;
+ };
+
// Class in charge of comparing manifest entries
struct ManifestComparator
{
ManifestComparator(const Execution::Context& context, const Repository::IPackageVersion::Metadata& installationMetadata);
// Gets the best installer from the manifest, if at least one is applicable.
- std::optional<Manifest::ManifestInstaller> GetPreferredInstaller(const Manifest::Manifest& manifest);
+ InstallerAndInapplicabilities GetPreferredInstaller(const Manifest::Manifest& manifest);
// Determines if an installer is applicable.
- bool IsApplicable(const Manifest::ManifestInstaller& installer);
+ InapplicabilityFlags IsApplicable(const Manifest::ManifestInstaller& installer);
// Determines if the first installer is a better choice.
bool IsFirstBetter(
diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp
@@ -42,6 +42,7 @@ namespace AppInstaller::CLI::Workflow
Utility::Version installedVersion = Utility::Version(installedPackage->GetProperty(PackageVersionProperty::Version));
ManifestComparator manifestComparator(context, installedPackage->GetMetadata());
bool updateFound = false;
+ bool installedTypeInapplicable = false;
// The version keys should have already been sorted by version
const auto& versionKeys = package->GetAvailableVersionKeys();
@@ -54,9 +55,16 @@ namespace AppInstaller::CLI::Workflow
auto manifest = packageVersion->GetManifest();
// Check applicable Installer
- auto installer = manifestComparator.GetPreferredInstaller(manifest);
+ auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(manifest);
if (!installer.has_value())
{
+ // If there is at least one installer whose only reason is InstalledType.
+ auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), InapplicabilityFlags::InstalledType);
+ if (onlyInstalledType != inapplicabilities.end())
+ {
+ installedTypeInapplicable = true;
+ }
+
continue;
}
@@ -80,8 +88,16 @@ namespace AppInstaller::CLI::Workflow
{
if (m_reportUpdateNotFound)
{
- context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl;
+ if (installedTypeInapplicable)
+ {
+ context.Reporter.Info() << Resource::String::UpgradeDifferentInstallTechnologyInNewerVersions << std::endl;
+ }
+ else
+ {
+ context.Reporter.Info() << Resource::String::UpdateNotApplicable << std::endl;
+ }
}
+
AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
}
}
diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp
@@ -971,7 +971,19 @@ namespace AppInstaller::CLI::Workflow
context.Add<Execution::Data::AllowedArchitectures>({ Utility::ConvertToArchitectureEnum(std::string(context.Args.GetArg(Execution::Args::Type::InstallArchitecture))) });
}
ManifestComparator manifestComparator(context, installationMetadata);
- context.Add<Execution::Data::Installer>(manifestComparator.GetPreferredInstaller(context.Get<Execution::Data::Manifest>()));
+ auto [installer, inapplicabilities] = manifestComparator.GetPreferredInstaller(context.Get<Execution::Data::Manifest>());
+
+ if (!installer.has_value())
+ {
+ auto onlyInstalledType = std::find(inapplicabilities.begin(), inapplicabilities.end(), InapplicabilityFlags::InstalledType);
+ if (onlyInstalledType != inapplicabilities.end())
+ {
+ context.Reporter.Info() << Resource::String::UpgradeDifferentInstallTechnology << std::endl;
+ AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
+ }
+ }
+
+ context.Add<Execution::Data::Installer>(installer);
}
void EnsureRunningAsAdmin(Execution::Context& context)
diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
@@ -1237,6 +1237,12 @@ Please specify one of them using the `--source` option to proceed.</value>
<data name="CountOutOfBoundsError" xml:space="preserve">
<value>The requested number of results must be between 1 and 1000.</value>
</data>
+ <data name="UpgradeDifferentInstallTechnologyInNewerVersions" xml:space="preserve">
+ <value>A newer version was found, but the install technology is different from the current version installed. Please uninstall the package and install the newer version.</value>
+ </data>
+ <data name="UpgradeDifferentInstallTechnology" xml:space="preserve">
+ <value>The install technology of the newer version specified is different from the current version installed. Please uninstall the package and install the newer version.</value>
+ </data>
<data name="WindowsPackageManagerPreview" xml:space="preserve">
<value>Windows Package Manager (Preview)</value>
<comment>The product name plus an indicator that this is a pre-release version.</comment>
diff --git a/src/AppInstallerCLITests/ManifestComparator.cpp b/src/AppInstallerCLITests/ManifestComparator.cpp
@@ -48,15 +48,26 @@ void RequireInstaller(const std::optional<ManifestInstaller>& actual, const Mani
REQUIRE(actual->Locale == expected.Locale);
}
+void RequireInapplicabilities(const std::vector<InapplicabilityFlags>& inapplicabilities, const std::vector<InapplicabilityFlags>& expected)
+{
+ REQUIRE(inapplicabilities.size() == expected.size());
+
+ for (std::size_t i = 0; i < inapplicabilities.size(); i++)
+ {
+ REQUIRE(inapplicabilities[i] == expected[i]);
+ }
+}
+
TEST_CASE("ManifestComparator_OSFilter_Low", "[manifest_comparator]")
{
Manifest manifest;
AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.99999.0");
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
REQUIRE(!result);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::OSVersion });
}
TEST_CASE("ManifestComparator_OSFilter_High", "[manifest_comparator]")
@@ -65,9 +76,10 @@ TEST_CASE("ManifestComparator_OSFilter_High", "[manifest_comparator]")
ManifestInstaller expected = AddInstaller(manifest, Architecture::Neutral, InstallerTypeEnum::Exe, ScopeEnum::Unknown, "10.0.0.0");
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, expected);
+ REQUIRE(inapplicabilities.size() == 0);
}
TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparator]")
@@ -78,10 +90,11 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato
SECTION("Nothing Installed")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, unknown);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("User Installed")
{
@@ -89,9 +102,10 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato
metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, unknown);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("Machine Installed")
{
@@ -99,9 +113,10 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter_Uknown", "[manifest_comparato
metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, unknown);
+ REQUIRE(inapplicabilities.size() == 0);
}
}
@@ -114,10 +129,11 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]")
SECTION("Nothing Installed")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, user);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("User Installed")
{
@@ -125,9 +141,10 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::User);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, user);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope });
}
SECTION("Machine Installed")
{
@@ -135,9 +152,10 @@ TEST_CASE("ManifestComparator_InstalledScopeFilter", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledScope] = ScopeToString(ScopeEnum::Machine);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, machine);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledScope });
}
}
@@ -150,10 +168,11 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]")
SECTION("Nothing Installed")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, msi);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("MSI Installed")
{
@@ -161,9 +180,10 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msi);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, msi);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType });
}
SECTION("MSIX Installed")
{
@@ -171,9 +191,10 @@ TEST_CASE("ManifestComparator_InstalledTypeFilter", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, msix);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledType });
}
}
@@ -186,10 +207,11 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]")
SECTION("Nothing Installed")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, burn);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("Exe Installed")
{
@@ -197,9 +219,10 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Exe);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, exe);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("Inno Installed")
{
@@ -207,9 +230,10 @@ TEST_CASE("ManifestComparator_InstalledTypeCompare", "[manifest_comparator]")
metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Inno);
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, burn);
+ REQUIRE(inapplicabilities.size() == 0);
}
}
@@ -222,10 +246,11 @@ TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]")
SECTION("Nothing Required")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, user);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("User Required")
{
@@ -233,9 +258,10 @@ TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]")
context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::User));
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, user);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope });
}
SECTION("Machine Required")
{
@@ -243,9 +269,10 @@ TEST_CASE("ManifestComparator_ScopeFilter", "[manifest_comparator]")
context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine));
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, machine);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Scope });
}
}
@@ -258,10 +285,11 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]")
SECTION("No Preference")
{
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// The default preference is user
RequireInstaller(result, user);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("User Preference")
{
@@ -269,9 +297,10 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]")
settings.Set<Setting::InstallScopePreference>(ScopePreference::User);
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, user);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("Machine Preference")
{
@@ -279,9 +308,10 @@ TEST_CASE("ManifestComparator_ScopeCompare", "[manifest_comparator]")
settings.Set<Setting::InstallScopePreference>(ScopePreference::Machine);
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, machine);
+ REQUIRE(inapplicabilities.size() == 0);
}
}
@@ -297,10 +327,11 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp
settings.Set<Setting::InstallLocalePreference>({ "en-US" });
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, enGB);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("en-US Installed")
{
@@ -308,9 +339,10 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp
metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, enGB);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("zh-CN Installed")
{
@@ -318,9 +350,10 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator_Uknown", "[manifest_comp
metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN";
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, unknown);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale });
}
}
@@ -336,10 +369,11 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator]
settings.Set<Setting::InstallLocalePreference>({ "en-US" });
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
// Only because it is first
RequireInstaller(result, enGB);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("en-US Installed")
{
@@ -347,9 +381,10 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator]
metadata[PackageVersionMetadata::InstalledLocale] = "en-US";
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, enGB);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale });
}
SECTION("zh-CN Installed")
{
@@ -357,9 +392,10 @@ TEST_CASE("ManifestComparator_InstalledLocaleComparator", "[manifest_comparator]
metadata[PackageVersionMetadata::InstalledLocale] = "zh-CN";
ManifestComparator mc(ManifestComparatorTestContext{}, metadata);
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
REQUIRE(!result);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::InstalledLocale, InapplicabilityFlags::InstalledLocale });
}
}
@@ -376,9 +412,10 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]")
context.Args.AddArg(Args::Type::Locale, "en-GB"s);
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, enGB);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale });
}
SECTION("zh-CN Required")
{
@@ -386,9 +423,10 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]")
context.Args.AddArg(Args::Type::Locale, "zh-CN"s);
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
REQUIRE(!result);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::Locale , InapplicabilityFlags::Locale, InapplicabilityFlags::Locale });
}
SECTION("en-US Preference")
{
@@ -396,9 +434,10 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]")
settings.Set<Setting::InstallLocalePreference>({ "en-US" });
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, enGB);
+ REQUIRE(inapplicabilities.size() == 0);
}
SECTION("zh-CN Preference")
{
@@ -406,9 +445,10 @@ TEST_CASE("ManifestComparator_LocaleComparator", "[manifest_comparator]")
settings.Set<Setting::InstallLocalePreference>({ "zh-CN" });
ManifestComparator mc(ManifestComparatorTestContext{}, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, unknown);
+ REQUIRE(inapplicabilities.size() == 0);
}
}
@@ -421,9 +461,10 @@ TEST_CASE("ManifestComparator_AllowedArchitecture_x64_only", "[manifest_comparat
context.Add<Data::AllowedArchitectures>({ Architecture::X64 });
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
REQUIRE(!result);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture });
}
TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]")
@@ -440,9 +481,10 @@ TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]")
context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::X64 });
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, x86);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture, InapplicabilityFlags::MachineArchitecture });
}
SECTION("Unknown")
{
@@ -450,10 +492,11 @@ TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]")
context.Add<Data::AllowedArchitectures>({ Architecture::Unknown });
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
REQUIRE(result);
REQUIRE(result->Arch == GetApplicableArchitectures()[0]);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture, InapplicabilityFlags::MachineArchitecture });
}
SECTION("x86 and Unknown")
{
@@ -461,8 +504,31 @@ TEST_CASE("ManifestComparator_AllowedArchitecture", "[manifest_comparator]")
context.Add<Data::AllowedArchitectures>({ Architecture::X86, Architecture::Unknown });
ManifestComparator mc(context, {});
- auto result = mc.GetPreferredInstaller(manifest);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
RequireInstaller(result, x86);
+ RequireInapplicabilities(inapplicabilities, { InapplicabilityFlags::MachineArchitecture, InapplicabilityFlags::MachineArchitecture });
}
}
+
+TEST_CASE("ManifestComparator_Inapplicabilities", "[manifest_comparator]")
+{
+ Manifest manifest;
+ ManifestInstaller installer = AddInstaller(manifest, Architecture::Arm64, InstallerTypeEnum::Exe, ScopeEnum::User, "10.0.99999.0", "es-MX");
+
+ ManifestComparatorTestContext context;
+ context.Add<Data::AllowedArchitectures>({ Architecture::X86 });
+ context.Args.AddArg(Args::Type::InstallScope, ScopeToString(ScopeEnum::Machine));
+ context.Args.AddArg(Args::Type::Locale, "en-GB"s);
+
+ IPackageVersion::Metadata metadata;
+ metadata[PackageVersionMetadata::InstalledType] = InstallerTypeToString(InstallerTypeEnum::Msix);
+
+ ManifestComparator mc(context, metadata);
+ auto [result, inapplicabilities] = mc.GetPreferredInstaller(manifest);
+
+ REQUIRE(!result);
+ RequireInapplicabilities(
+ inapplicabilities,
+ { InapplicabilityFlags::OSVersion | InapplicabilityFlags::InstalledType | InapplicabilityFlags::Locale | InapplicabilityFlags::Scope | InapplicabilityFlags::MachineArchitecture });
+}
diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp
@@ -1434,7 +1434,27 @@ TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicable", "[UpdateFlow][workfl
// Verify Installer is not called.
REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
- REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpdateNotApplicable).get()) != std::string::npos);
+ REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnologyInNewerVersions).get()) != std::string::npos);
+ REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
+}
+
+TEST_CASE("UpdateFlow_UpdateExeInstallerTypeNotApplicableSpecificVersion", "[UpdateFlow][workflow]")
+{
+ TestCommon::TempFile updateResultPath("TestExeInstalled.txt");
+
+ std::ostringstream updateOutput;
+ TestContext context{ updateOutput, std::cin };
+ OverrideForCompositeInstalledSource(context);
+ context.Args.AddArg(Execution::Args::Type::Query, "TestExeInstallerWithIncompatibleInstallerType"sv);
+ context.Args.AddArg(Execution::Args::Type::Version, "2.0.0.0"sv);
+
+ UpgradeCommand update({});
+ update.Execute(context);
+ INFO(updateOutput.str());
+
+ // Verify Installer is not called.
+ REQUIRE(!std::filesystem::exists(updateResultPath.GetPath()));
+ REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::UpgradeDifferentInstallTechnology).get()) != std::string::npos);
REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE);
}