diff --git a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp index c0f33b9e51..7bbcdebc61 100644 --- a/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp +++ b/src/AppInstallerCLICore/ConfigurationWingetDscModuleUnitValidation.cpp @@ -358,7 +358,8 @@ namespace AppInstaller::CLI::Configuration { if (!package.Version.empty()) { - auto versionKeys = searchResult.Matches.at(0).Package->GetAvailableVersionKeys(); + std::shared_ptr availablePackage = searchResult.Matches.at(0).Package->GetAvailable().at(0); + auto versionKeys = availablePackage->GetVersionKeys(); bool foundVersion = false; for (auto const& versionKey : versionKeys) { diff --git a/src/AppInstallerCLICore/ExecutionContextData.h b/src/AppInstallerCLICore/ExecutionContextData.h index 146d0006cb..e19b8d7233 100644 --- a/src/AppInstallerCLICore/ExecutionContextData.h +++ b/src/AppInstallerCLICore/ExecutionContextData.h @@ -105,7 +105,7 @@ namespace AppInstaller::CLI::Execution template <> struct DataMapping { - using value_t = std::shared_ptr; + using value_t = std::shared_ptr; }; template <> diff --git a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp index 6379ef2af9..57789b8b1d 100644 --- a/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/CompletionFlow.cpp @@ -70,11 +70,14 @@ namespace AppInstaller::CLI::Workflow const std::string& word = context.Get().Word(); auto stream = context.Reporter.Completion(); - for (const auto& vc : context.Get()->GetAvailableVersionKeys()) + for (const auto& ap : context.Get()->GetAvailable()) { - if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) + for (const auto& vc : ap->GetVersionKeys()) { - OutputCompletionString(stream, vc.Version); + if (word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Version, word)) + { + OutputCompletionString(stream, vc.Version); + } } } } @@ -86,12 +89,15 @@ namespace AppInstaller::CLI::Workflow std::vector channels; - for (const auto& vc : context.Get()->GetAvailableVersionKeys()) + for (const auto& ap : context.Get()->GetAvailable()) { - if ((word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Channel, word)) && - std::find(channels.begin(), channels.end(), vc.Channel) == channels.end()) + for (const auto& vc : ap->GetVersionKeys()) { - channels.emplace_back(vc.Channel); + if ((word.empty() || Utility::ICUCaseInsensitiveStartsWith(vc.Channel, word)) && + std::find(channels.begin(), channels.end(), vc.Channel) == channels.end()) + { + channels.emplace_back(vc.Channel); + } } } diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index 5b5a8898a6..93a52f1733 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -4,6 +4,7 @@ #include "DependencyNodeProcessor.h" #include "ManifestComparator.h" #include +#include using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -41,20 +42,21 @@ namespace AppInstaller::CLI::Workflow const auto& match = matches.at(0); const auto& package = match.Package; auto packageId = package->GetProperty(PackageProperty::Id); - m_nodePackageInstalledVersion = package->GetInstalledVersion(); + m_nodePackageInstalledVersion = GetInstalledVersion(package); + std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); if (m_context.Args.Contains(Execution::Args::Type::Force)) { - m_nodePackageLatestVersion = package->GetLatestAvailableVersion(); + m_nodePackageLatestVersion = availableVersions->GetLatestVersion(); } else { Pinning::PinBehavior pinBehavior = m_context.Args.Contains(Execution::Args::Type::IncludePinned) ? Pinning::PinBehavior::IncludePinned : Pinning::PinBehavior::ConsiderPins; Pinning::PinningData pinningData{ Pinning::PinningData::Disposition::ReadOnly }; - auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, m_nodePackageInstalledVersion); - m_nodePackageLatestVersion = evaluator.GetLatestAvailableVersionForPins(package); + m_nodePackageLatestVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); } if (m_nodePackageInstalledVersion && dependencyNode.IsVersionOk(Utility::Version(m_nodePackageInstalledVersion->GetProperty(PackageVersionProperty::Version)))) diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index 5a00ebe631..06c87eb73e 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -9,6 +9,7 @@ #include "WorkflowBase.h" #include #include +#include namespace AppInstaller::CLI::Workflow { @@ -60,20 +61,22 @@ namespace AppInstaller::CLI::Workflow // If requested, checks that the installed version is available and reports a warning if it is not. std::shared_ptr GetAvailableVersionForInstalledPackage( Execution::Context& context, - std::shared_ptr package, + std::shared_ptr package, Utility::LocIndView version, Utility::LocIndView channel, bool checkVersion) { + std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); + if (!checkVersion) { - return package->GetLatestAvailableVersion(); + return availableVersions->GetLatestVersion(); } - auto availablePackageVersion = package->GetAvailableVersion({ "", version, channel }); + auto availablePackageVersion = availableVersions->GetVersion({ "", version, channel }); if (!availablePackageVersion) { - availablePackageVersion = package->GetLatestAvailableVersion(); + availablePackageVersion = availableVersions->GetLatestVersion(); if (availablePackageVersion) { // Warn installed version is not available. @@ -100,7 +103,7 @@ namespace AppInstaller::CLI::Workflow auto& exportedSources = exportedPackages.Sources; for (const auto& packageMatch : searchResult.Matches) { - auto installedPackageVersion = packageMatch.Package->GetInstalledVersion(); + auto installedPackageVersion = GetInstalledVersion(packageMatch.Package); auto version = installedPackageVersion->GetProperty(PackageVersionProperty::Version); auto channel = installedPackageVersion->GetProperty(PackageVersionProperty::Channel); diff --git a/src/AppInstallerCLICore/Workflows/PinFlow.cpp b/src/AppInstallerCLICore/Workflows/PinFlow.cpp index 3c571d72bb..f74bece11b 100644 --- a/src/AppInstallerCLICore/Workflows/PinFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/PinFlow.cpp @@ -6,6 +6,7 @@ #include "TableOutput.h" #include #include +#include using namespace AppInstaller::Repository; @@ -38,7 +39,7 @@ namespace AppInstaller::CLI::Workflow if (context.Args.Contains(Execution::Args::Type::PinInstalled)) { - auto installedVersion = package->GetInstalledVersion(); + auto installedVersion = GetInstalledVersion(package); if (installedVersion) { pinKeys.emplace(Pinning::PinKey::GetPinKeyForInstalled(installedVersion->GetProperty(PackageVersionProperty::Id))); @@ -46,13 +47,12 @@ namespace AppInstaller::CLI::Workflow } else { - auto packageVersionKeys = package->GetAvailableVersionKeys(); - for (const auto& versionKey : packageVersionKeys) + auto availablePackages = package->GetAvailable(); + for (const auto& availablePackage : availablePackages) { - auto availableVersion = package->GetAvailableVersion(versionKey); pinKeys.emplace( - availableVersion->GetProperty(PackageVersionProperty::Id).get(), - availableVersion->GetProperty(PackageVersionProperty::SourceIdentifier).get()); + availablePackage->GetProperty(PackageProperty::Id).get(), + availablePackage->GetSource().GetIdentifier()); } } @@ -140,7 +140,7 @@ namespace AppInstaller::CLI::Workflow } else { - auto availableVersion = package->GetAvailableVersion({ pinKey.SourceId, "", "" }); + auto availableVersion = GetAvailablePackageFromSource(package, pinKey.SourceId)->GetLatestVersion(); if (availableVersion) { packageNameToReport = availableVersion->GetProperty(PackageVersionProperty::Name); @@ -198,8 +198,6 @@ namespace AppInstaller::CLI::Workflow // Note that if a source was specified in the command line, // that will be the only one we get version keys from. // So, we remove pins from all sources unless one was provided. - auto packageVersionKeys = package->GetAvailableVersionKeys(); - for (const auto& pin : pins) { AICLI_LOG(CLI, Info, << "Removing Pin " << pin.GetKey().ToString()); @@ -255,15 +253,19 @@ namespace AppInstaller::CLI::Workflow else { // This ensures we get the info from the right source if it exists on multiple - auto availableVersion = match.Package->GetAvailableVersion({ pinKey.SourceId, "", "" }); - if (availableVersion) + auto availablePackage = GetAvailablePackageFromSource(match.Package, pinKey.SourceId); + if (availablePackage) { - packageName = availableVersion->GetProperty(PackageVersionProperty::Name); - sourceName = availableVersion->GetProperty(PackageVersionProperty::SourceName); + auto availableVersion = availablePackage->GetLatestVersion(); + if (availableVersion) + { + packageName = availableVersion->GetProperty(PackageVersionProperty::Name); + sourceName = availableVersion->GetProperty(PackageVersionProperty::SourceName); + } } } - auto installedVersion = match.Package->GetInstalledVersion(); + auto installedVersion = GetInstalledVersion(match.Package); if (installedVersion) { packageName = installedVersion->GetProperty(PackageVersionProperty::Name); diff --git a/src/AppInstallerCLICore/Workflows/RepairFlow.cpp b/src/AppInstallerCLICore/Workflows/RepairFlow.cpp index 2b76475198..f4158c4622 100644 --- a/src/AppInstallerCLICore/Workflows/RepairFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/RepairFlow.cpp @@ -14,6 +14,7 @@ #include "AppInstallerSynchronization.h" #include "MSStoreInstallerHandler.h" #include "ManifestComparator.h" +#include using namespace AppInstaller::Manifest; using namespace AppInstaller::Msix; @@ -443,10 +444,11 @@ namespace AppInstaller::CLI::Workflow std::string_view requestedVersion = context.Args.Contains(Execution::Args::Type::Version) ? context.Args.GetArg(Execution::Args::Type::Version) : installedVersion.ToString(); // If it's Store source with only one version unknown, use the unknown version for available version mapping. const auto& package = context.Get(); - auto versionKeys = package->GetAvailableVersionKeys(); + auto packageVersions = GetAvailableVersionsForInstalledVersion(package, installedPackage); + auto versionKeys = packageVersions->GetVersionKeys(); if (versionKeys.size() == 1) { - auto packageVersion = package->GetAvailableVersion(versionKeys.at(0)); + auto packageVersion = packageVersions->GetVersion(versionKeys.at(0)); if (packageVersion->GetSource().IsWellKnownSource(WellKnownSource::MicrosoftStore) && Utility::Version{ packageVersion->GetProperty(PackageVersionProperty::Version) }.IsUnknown()) { diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 2331b89be2..225e58cd64 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -11,6 +11,7 @@ #include #include #include +#include using namespace AppInstaller::CLI::Execution; using namespace AppInstaller::Manifest; @@ -34,9 +35,8 @@ namespace AppInstaller::CLI::Workflow std::string SourceIdentifier; }; - void AddIfRemoteAndNotPresent(const std::shared_ptr& packageVersion) + void AddIfRemoteAndNotPresent(Source&& source, const Utility::LocIndString& identifier) { - auto source = packageVersion->GetSource(); const auto details = source.GetDetails(); if (!source.ContainsAvailablePackages()) { @@ -51,7 +51,17 @@ namespace AppInstaller::CLI::Workflow } } - Items.emplace_back(Item{ packageVersion->GetProperty(PackageVersionProperty::Id), std::move(source), details.Identifier }); + Items.emplace_back(Item{ identifier, std::move(source), details.Identifier }); + } + + void AddIfRemoteAndNotPresent(const std::shared_ptr& packageVersion) + { + AddIfRemoteAndNotPresent(packageVersion->GetSource(), packageVersion->GetProperty(PackageVersionProperty::Id)); + } + + void AddIfRemoteAndNotPresent(const std::shared_ptr& package) + { + AddIfRemoteAndNotPresent(package->GetSource(), package->GetProperty(PackageProperty::Id)); } std::vector Items; @@ -311,12 +321,12 @@ namespace AppInstaller::CLI::Workflow UninstallCorrelatedSources correlatedSources; // Start with the installed version - correlatedSources.AddIfRemoteAndNotPresent(package->GetInstalledVersion()); + correlatedSources.AddIfRemoteAndNotPresent(GetInstalledVersion(package)); // Then look through all available versions - for (const auto& versionKey : package->GetAvailableVersionKeys()) + for (const auto& availablePackage : package->GetAvailable()) { - correlatedSources.AddIfRemoteAndNotPresent(package->GetAvailableVersion(versionKey)); + correlatedSources.AddIfRemoteAndNotPresent(availablePackage); } // Finally record the uninstall for each found value diff --git a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp index edcfe4d6d9..4c72c468d5 100644 --- a/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UpdateFlow.cpp @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - #include "pch.h" #include "WorkflowBase.h" #include "DependenciesFlow.h" @@ -8,6 +7,7 @@ #include "UpdateFlow.h" #include "ManifestComparator.h" #include +#include using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Microsoft; @@ -72,10 +72,11 @@ namespace AppInstaller::CLI::Workflow const bool includePinned = m_isSinglePackage || context.Args.Contains(Execution::Args::Type::IncludePinned); PinningData pinningData{ PinningData::Disposition::ReadOnly }; - auto evaluator = pinningData.CreatePinStateEvaluator(includePinned ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins, package->GetInstalledVersion()); + auto evaluator = pinningData.CreatePinStateEvaluator(includePinned ? PinBehavior::IncludePinned : PinBehavior::ConsiderPins, GetInstalledVersion(package)); // The version keys should have already been sorted by version - const auto& versionKeys = package->GetAvailableVersionKeys(); + auto availableVersions = GetAvailableVersionsForInstalledVersion(package); + const auto& versionKeys = availableVersions->GetVersionKeys(); // Assume that no update versions are applicable bool upgradeVersionAvailable = false; for (const auto& key : versionKeys) @@ -89,7 +90,7 @@ namespace AppInstaller::CLI::Workflow upgradeVersionAvailable = true; } - auto packageVersion = package->GetAvailableVersion(key); + auto packageVersion = availableVersions->GetVersion(key); // Check if the package is pinned PinType pinType = evaluator.EvaluatePinType(packageVersion); @@ -217,7 +218,7 @@ namespace AppInstaller::CLI::Workflow auto updateContextPtr = context.CreateSubContext(); Execution::Context& updateContext = *updateContextPtr; auto previousThreadGlobals = updateContext.SetForCurrentThread(); - auto installedVersion = match.Package->GetInstalledVersion(); + auto installedVersion = GetInstalledVersion(match.Package); updateContext.Add(match.Package); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 6fd7818a9a..b0045126af 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -11,6 +11,7 @@ #include #include #include +#include using namespace std::string_literals; using namespace AppInstaller::Utility::literals; @@ -628,7 +629,7 @@ namespace AppInstaller::CLI::Workflow for (size_t i = 0; i < searchResult.Matches.size(); ++i) { - auto latestVersion = searchResult.Matches[i].Package->GetLatestAvailableVersion(); + auto latestVersion = GetAllAvailableVersions(searchResult.Matches[i].Package)->GetLatestVersion(); table.OutputLine({ latestVersion->GetProperty(PackageVersionProperty::Name), @@ -739,10 +740,10 @@ namespace AppInstaller::CLI::Workflow auto package = searchResult.Matches[i].Package; std::string sourceName; - auto latest = package->GetLatestAvailableVersion(); - if (latest) + auto available = package->GetAvailable(); + if (!available.empty()) { - auto source = latest->GetSource(); + auto source = available[0]->GetSource(); if (source) { sourceName = source.GetDetails().Name; @@ -799,13 +800,14 @@ namespace AppInstaller::CLI::Workflow for (const auto& match : searchResult.Matches) { - auto installedVersion = match.Package->GetInstalledVersion(); + auto installedVersion = GetInstalledVersion(match.Package); if (installedVersion) { auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, installedVersion); + auto availableVersions = GetAvailableVersionsForInstalledVersion(match.Package, installedVersion); - auto latestVersion = evaluator.GetLatestAvailableVersionForPins(match.Package); + auto latestVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); bool updateAvailable = evaluator.IsUpdate(latestVersion); bool updateIsPinned = false; @@ -819,7 +821,7 @@ namespace AppInstaller::CLI::Workflow if (m_onlyShowUpgrades && !updateAvailable) { // Reuse the evaluator to check if there is an update outside of the pinning - auto unpinnedLatestVersion = match.Package->GetLatestAvailableVersion(); + auto unpinnedLatestVersion = availableVersions->GetLatestVersion(); bool updateAvailableWithoutPins = evaluator.IsUpdate(unpinnedLatestVersion); if (updateAvailableWithoutPins) @@ -996,7 +998,7 @@ namespace AppInstaller::CLI::Workflow AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND); } - std::shared_ptr package = searchResult.Matches.at(0).Package; + std::shared_ptr package = searchResult.Matches.at(0).Package; Logging::Telemetry().LogAppFound(package->GetProperty(PackageProperty::Name), package->GetProperty(PackageProperty::Id)); context.Add(std::move(package)); @@ -1007,8 +1009,9 @@ namespace AppInstaller::CLI::Workflow { PackageVersionKey key("", m_version, m_channel); - std::shared_ptr package = context.Get(); + std::shared_ptr package = context.Get(); std::shared_ptr requestedVersion; + auto availableVersions = GetAvailableVersionsForInstalledVersion(package); if (m_considerPins) { @@ -1026,17 +1029,17 @@ namespace AppInstaller::CLI::Workflow } PinningData pinningData{ PinningData::Disposition::ReadOnly }; - auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, GetInstalledVersion(package)); // TODO: The logic here will probably have to get more difficult once we support channels if (Utility::IsEmptyOrWhitespace(m_version) && Utility::IsEmptyOrWhitespace(m_channel)) { - requestedVersion = evaluator.GetLatestAvailableVersionForPins(package); + requestedVersion = evaluator.GetLatestAvailableVersionForPins(availableVersions); if (!requestedVersion) { // Check whether we didn't find the latest version because it was pinned or because there wasn't one - auto latestVersion = package->GetLatestAvailableVersion(); + auto latestVersion = availableVersions->GetLatestVersion(); if (latestVersion) { isPinned = true; @@ -1045,7 +1048,7 @@ namespace AppInstaller::CLI::Workflow } else { - requestedVersion = package->GetAvailableVersion(key); + requestedVersion = availableVersions->GetVersion(key); isPinned = evaluator.EvaluatePinType(requestedVersion) != PinType::Unknown; } @@ -1066,7 +1069,7 @@ namespace AppInstaller::CLI::Workflow else { // The simple case: Just look up the requested version - requestedVersion = package->GetAvailableVersion(key); + requestedVersion = availableVersions->GetVersion(key); } std::optional manifest; @@ -1346,7 +1349,7 @@ namespace AppInstaller::CLI::Workflow void GetInstalledPackageVersion(Execution::Context& context) { - context.Add(context.Get()->GetInstalledVersion()); + context.Add(GetInstalledVersion(context.Get())); } void ReportExecutionStage::operator()(Execution::Context& context) const @@ -1356,7 +1359,7 @@ namespace AppInstaller::CLI::Workflow void ShowAppVersions(Execution::Context& context) { - auto versions = context.Get()->GetAvailableVersionKeys(); + auto versions = GetAllAvailableVersions(context.Get())->GetVersionKeys(); Execution::TableOutput<2> table(context.Reporter, { Resource::String::ShowVersion, Resource::String::ShowChannel }); for (const auto& version : versions) diff --git a/src/AppInstallerCLITests/ARPChanges.cpp b/src/AppInstallerCLITests/ARPChanges.cpp index eaf0478275..04728aa9d3 100644 --- a/src/AppInstallerCLITests/ARPChanges.cpp +++ b/src/AppInstallerCLITests/ARPChanges.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using namespace TestCommon; using namespace AppInstaller; @@ -133,7 +134,7 @@ struct ARPTestContext : public Context AddResult(MatchResult, id, name, publisher, version); } - void ExpectEvent(size_t arpChanges, size_t matches, size_t overlap, IPackage* arpEntry = nullptr) + void ExpectEvent(size_t arpChanges, size_t matches, size_t overlap, const std::shared_ptr& arpEntry = nullptr) { REQUIRE(Logger->WasLogSuccessfulInstallARPChangeCalled); @@ -149,7 +150,7 @@ struct ARPTestContext : public Context if (arpEntry) { - auto version = arpEntry->GetInstalledVersion(); + auto version = GetInstalledVersion(arpEntry); REQUIRE(version->GetProperty(PackageVersionProperty::Name) == ARPName); REQUIRE(version->GetProperty(PackageVersionProperty::Version) == ARPVersion); @@ -202,7 +203,7 @@ struct ARPTestContext : public Context TestPackage::MetadataMap metadata; metadata[PackageVersionMetadata::Publisher] = publisher; - result.Matches.emplace_back(TestPackage::Make(manifest, std::move(metadata), std::vector{}, Source), defaultFilter); + result.Matches.emplace_back(TestCompositePackage::Make(manifest, std::move(metadata), std::vector{}, Source), defaultFilter); } }; @@ -259,8 +260,8 @@ TEST_CASE("ARPChanges_CheckSnapshot", "[ARPChanges][workflow]") { if (match.Package->GetProperty(PackageProperty::Id) == std::get<0>(*itr)) { - REQUIRE(match.Package->GetInstalledVersion()->GetProperty(PackageVersionProperty::Version) == std::get<1>(*itr)); - REQUIRE(match.Package->GetInstalledVersion()->GetProperty(PackageVersionProperty::Channel) == std::get<2>(*itr)); + REQUIRE(GetInstalledVersion(match.Package)->GetProperty(PackageVersionProperty::Version) == std::get<1>(*itr)); + REQUIRE(GetInstalledVersion(match.Package)->GetProperty(PackageVersionProperty::Channel) == std::get<2>(*itr)); snapshot.erase(itr); found = true; @@ -296,7 +297,7 @@ TEST_CASE("ARPChanges_NoChange_SingleMatch", "[ARPChanges][workflow]") context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; - context.ExpectEvent(0, 1, 0, context.MatchResult.Matches[0].Package.get()); + context.ExpectEvent(0, 1, 0, context.MatchResult.Matches[0].Package); } TEST_CASE("ARPChanges_NoChange_MultiMatch", "[ARPChanges][workflow]") @@ -340,7 +341,7 @@ TEST_CASE("ARPChanges_SingleChange_SingleMatch", "[ARPChanges][workflow]") context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; - context.ExpectEvent(1, 1, 0, context.MatchResult.Matches.back().Package.get()); + context.ExpectEvent(1, 1, 0, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_SingleChange_MultiMatch", "[ARPChanges][workflow]") @@ -356,7 +357,7 @@ TEST_CASE("ARPChanges_SingleChange_MultiMatch", "[ARPChanges][workflow]") context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; - context.ExpectEvent(1, 2, 1, context.EverythingResult.Matches.back().Package.get()); + context.ExpectEvent(1, 2, 1, context.EverythingResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_NoMatch", "[ARPChanges][workflow]") @@ -387,7 +388,7 @@ TEST_CASE("ARPChanges_MultiChange_SingleMatch_NoOverlap", "[ARPChanges][workflow context.AddMatchResult("MatchId1", "MatchName1", "MatchPublisher1", "MatchVersion1"); context << ReportARPChanges; - context.ExpectEvent(2, 1, 0, context.MatchResult.Matches.back().Package.get()); + context.ExpectEvent(2, 1, 0, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_SingleMatch_Overlap", "[ARPChanges][workflow]") @@ -403,7 +404,7 @@ TEST_CASE("ARPChanges_MultiChange_SingleMatch_Overlap", "[ARPChanges][workflow]" context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; - context.ExpectEvent(2, 1, 1, context.MatchResult.Matches.back().Package.get()); + context.ExpectEvent(2, 1, 1, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_MultiMatch_NoOverlap", "[ARPChanges][workflow]") @@ -437,7 +438,7 @@ TEST_CASE("ARPChanges_MultiChange_MultiMatch_SingleOverlap", "[ARPChanges][workf context.MatchResult.Matches.emplace_back(context.EverythingResult.Matches.back()); context << ReportARPChanges; - context.ExpectEvent(2, 2, 1, context.MatchResult.Matches.back().Package.get()); + context.ExpectEvent(2, 2, 1, context.MatchResult.Matches.back().Package); } TEST_CASE("ARPChanges_MultiChange_MultiMatch_MultiOverlap", "[ARPChanges][workflow]") diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp index 9e6501759e..dbbddb7cff 100644 --- a/src/AppInstallerCLITests/CompositeSource.cpp +++ b/src/AppInstallerCLITests/CompositeSource.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace std::string_literals; using namespace std::string_view_literals; @@ -159,17 +160,17 @@ struct TestPackageHelper return *this; } - operator std::shared_ptr() + operator std::shared_ptr() { if (!m_package) { if (m_isInstalled) { - m_package = TestPackage::Make(m_manifest, TestPackage::MetadataMap{}, std::vector(), m_source); + m_package = TestCompositePackage::Make(m_manifest, TestCompositePackage::MetadataMap{}, std::vector(), m_source); } else { - m_package = TestPackage::Make(std::vector{ m_manifest }, m_source, m_hideSystemReferenceStrings); + m_package = TestCompositePackage::Make(std::vector{ m_manifest }, m_source, m_hideSystemReferenceStrings); } } @@ -185,7 +186,7 @@ struct TestPackageHelper bool m_isInstalled; Manifest::Manifest m_manifest; std::shared_ptr m_source; - std::shared_ptr m_package; + std::shared_ptr m_package; bool m_hideSystemReferenceStrings = false; }; @@ -231,8 +232,8 @@ TEST_CASE("CompositeSource_PackageFamilyName_NotAvailable", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().empty()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_PackageFamilyName_Available", "[CompositeSource]") @@ -253,8 +254,9 @@ TEST_CASE("CompositeSource_PackageFamilyName_Available", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_ProductCode_NotAvailable", "[CompositeSource]") @@ -267,8 +269,8 @@ TEST_CASE("CompositeSource_ProductCode_NotAvailable", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().empty()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_ProductCode_Available", "[CompositeSource]") @@ -289,8 +291,9 @@ TEST_CASE("CompositeSource_ProductCode_Available", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_NameAndPublisher_Match", "[CompositeSource]") @@ -309,8 +312,9 @@ TEST_CASE("CompositeSource_NameAndPublisher_Match", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_MultiMatch_FindsStrongMatch", "[CompositeSource]") @@ -330,10 +334,12 @@ TEST_CASE("CompositeSource_MultiMatch_FindsStrongMatch", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == name); - REQUIRE(!Version(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version)).IsUnknown()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); + auto version = result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); + REQUIRE(version->GetProperty(PackageVersionProperty::Name).get() == name); + REQUIRE(!Version(version->GetProperty(PackageVersionProperty::Version)).IsUnknown()); } TEST_CASE("CompositeSource_MultiMatch_DoesNotFindStrongMatch", "[CompositeSource]") @@ -351,8 +357,8 @@ TEST_CASE("CompositeSource_MultiMatch_DoesNotFindStrongMatch", "[CompositeSource SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 0); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_FoundByBothRootSearches", "[CompositeSource]") @@ -386,8 +392,9 @@ TEST_CASE("CompositeSource_FoundByBothRootSearches", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_OnlyAvailableFoundByRootSearch", "[CompositeSource]") @@ -417,8 +424,9 @@ TEST_CASE("CompositeSource_OnlyAvailableFoundByRootSearch", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_FoundByAvailableRootSearch_NotInstalled", "[CompositeSource]") @@ -465,8 +473,9 @@ TEST_CASE("CompositeSource_UpdateWithBetterMatchCriteria", "[CompositeSource]") SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); REQUIRE(result.Matches[0].MatchCriteria.Type == originalType); // Now make the source root search find it with a better criteria @@ -484,8 +493,9 @@ TEST_CASE("CompositeSource_UpdateWithBetterMatchCriteria", "[CompositeSource]") result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); REQUIRE(result.Matches[0].MatchCriteria.Type == type); } @@ -539,19 +549,27 @@ TEST_CASE("CompositePackage_AvailableVersions_ChannelFilteredOut", "[CompositeSo hasChannel.Version = "2.0"; SearchResult result; - result.Matches.emplace_back(TestPackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); - REQUIRE(result.Matches.back().Package->GetAvailableVersionKeys().size() == 2); + result.Matches.emplace_back(TestCompositePackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 2); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - auto versionKeys = result.Matches[0].Package->GetAvailableVersionKeys(); - REQUIRE(versionKeys.size() == 1); - REQUIRE(versionKeys[0].Channel.empty()); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + auto package = result.Matches[0].Package->GetAvailable()[0]; - auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(); + auto versionKeys = package->GetVersionKeys(); + REQUIRE(versionKeys.size() == 2); + + auto availableVersions = GetAvailableVersionsForInstalledVersion(result.Matches[0].Package); + auto availableVersionKeys = availableVersions->GetVersionKeys(); + REQUIRE(availableVersionKeys.size() == 1); + REQUIRE(availableVersionKeys[0].Channel.empty()); + + auto latestVersion = availableVersions->GetLatestVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get().empty()); } @@ -573,19 +591,27 @@ TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[Composite hasChannel.Version = "2.0"; SearchResult result; - result.Matches.emplace_back(TestPackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); - REQUIRE(result.Matches.back().Package->GetAvailableVersionKeys().size() == 2); + result.Matches.emplace_back(TestCompositePackage::Make(std::vector{ noChannel, hasChannel }, setup.Available), Criteria()); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 2); return result; }; SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - auto versionKeys = result.Matches[0].Package->GetAvailableVersionKeys(); - REQUIRE(versionKeys.size() == 1); - REQUIRE(versionKeys[0].Channel == channel); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + auto package = result.Matches[0].Package->GetAvailable()[0]; + + auto versionKeys = package->GetVersionKeys(); + REQUIRE(versionKeys.size() == 2); - auto latestVersion = result.Matches[0].Package->GetLatestAvailableVersion(); + auto availableVersions = GetAvailableVersionsForInstalledVersion(result.Matches[0].Package); + auto availableVersionKeys = availableVersions->GetVersionKeys(); + REQUIRE(availableVersionKeys.size() == 1); + REQUIRE(availableVersionKeys[0].Channel == channel); + + auto latestVersion = availableVersions->GetLatestVersion(); REQUIRE(latestVersion); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Channel).get() == channel); } @@ -625,9 +651,10 @@ TEST_CASE("CompositeSource_MultipleAvailableSources_MatchAll", "[CompositeSource SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 2); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == firstName); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 2); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetProperty(PackageProperty::Name).get() == firstName); + REQUIRE(result.Matches[0].Package->GetAvailable()[1]->GetProperty(PackageProperty::Name).get() == secondName); } TEST_CASE("CompositeSource_MultipleAvailableSources_MatchSecond", "[CompositeSource]") @@ -654,9 +681,9 @@ TEST_CASE("CompositeSource_MultipleAvailableSources_MatchSecond", "[CompositeSou SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name).get() == secondName); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetProperty(PackageProperty::Name).get() == secondName); } TEST_CASE("CompositeSource_MultipleAvailableSources_ReverseMatchBoth", "[CompositeSource]") @@ -684,15 +711,15 @@ TEST_CASE("CompositeSource_MultipleAvailableSources_ReverseMatchBoth", "[Composi SearchResult result = setup.Search(); REQUIRE(result.Matches.size() == 1); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetAvailableVersionKeys().size() == 1); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetVersionKeys().size() == 1); } TEST_CASE("CompositeSource_IsSame", "[CompositeSource]") { CompositeTestSetup setup; setup.Installed->Everything.Matches.emplace_back(MakeInstalled().WithPFN("sortof_apfn"), Criteria()); - setup.Available->Everything.Matches.emplace_back(MakeAvailable(setup.Available).WithPFN("sortof_apfn"), Criteria()); SearchResult result1 = setup.Search(); REQUIRE(result1.Matches.size() == 1); @@ -700,7 +727,8 @@ TEST_CASE("CompositeSource_IsSame", "[CompositeSource]") SearchResult result2 = setup.Search(); REQUIRE(result2.Matches.size() == 1); - REQUIRE(result1.Matches[0].Package->IsSame(result2.Matches[0].Package.get())); + REQUIRE(result1.Matches[0].Package->GetInstalled()); + REQUIRE(result1.Matches[0].Package->GetInstalled()->IsSame(result1.Matches[0].Package->GetInstalled().get())); } TEST_CASE("CompositeSource_AvailableSearchFailure", "[CompositeSource]") @@ -727,8 +755,9 @@ TEST_CASE("CompositeSource_AvailableSearchFailure", "[CompositeSource]") SearchResult result = Composite.Search({}); REQUIRE(result.Matches.size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); - auto pfns = result.Matches[0].Package->GetLatestAvailableVersion()->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); + auto pfns = result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetMultiProperty(PackageVersionMultiProperty::PackageFamilyName); REQUIRE(pfns.size() == 1); REQUIRE(pfns[0] == pfn); @@ -872,9 +901,11 @@ TEST_CASE("CompositeSource_TrackingPackageFound", "[CompositeSource]") REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()); } TEST_CASE("CompositeSource_TrackingPackageFound_MetadataPopulatedFromTracking", "[CompositeSource]") @@ -928,9 +959,9 @@ TEST_CASE("CompositeSource_TrackingPackageFound_MetadataPopulatedFromTracking", REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); - auto metadata = result.Matches[0].Package->GetInstalledVersion()->GetMetadata(); + auto metadata = GetInstalledVersion(result.Matches[0].Package)->GetMetadata(); REQUIRE(metadata[Repository::PackageVersionMetadata::UserIntentArchitecture] == "X86"); REQUIRE(metadata[Repository::PackageVersionMetadata::UserIntentLocale] == "en-US"); REQUIRE(metadata[Repository::PackageVersionMetadata::InstalledArchitecture] == "X86"); @@ -963,9 +994,10 @@ TEST_CASE("CompositeSource_TrackingFound_AvailableNot", "[CompositeSource]") REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(!result.Matches[0].Package->GetLatestAvailableVersion()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(result.Matches[0].Package->GetAvailable().empty()); } TEST_CASE("CompositeSource_TrackingFound_AvailablePath", "[CompositeSource]") @@ -1004,9 +1036,11 @@ TEST_CASE("CompositeSource_TrackingFound_AvailablePath", "[CompositeSource]") REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()); - REQUIRE(result.Matches[0].Package->GetInstalledVersion()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); - REQUIRE(result.Matches[0].Package->GetLatestAvailableVersion()); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + REQUIRE(result.Matches[0].Package->GetInstalled()->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)->GetSource().GetIdentifier() == setup.Available->Details.Identifier); + REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(result.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()); } TEST_CASE("CompositeSource_TrackingFound_NotInstalled", "[CompositeSource]") @@ -1043,8 +1077,7 @@ TEST_CASE("CompositeSource_NullAvailableVersion", "[CompositeSource]") setup.Available->Everything.Matches.emplace_back(MakeInstalled(), Criteria()); // We are mostly testing to see if a null available version causes an AV or not - SearchResult result = setup.Search(); - REQUIRE(result.Matches.size() == 1); + REQUIRE_THROWS_HR(setup.Search(), E_UNEXPECTED); } struct ExpectedResultForPinBehavior @@ -1071,17 +1104,18 @@ struct ExpectedResultsForPinning std::vector AvailableVersions; }; -void RequireExpectedResultsWithPin(std::shared_ptr package, const ExpectedResultsForPinning& expectedResult) +void RequireExpectedResultsWithPin(std::shared_ptr package, const ExpectedResultsForPinning& expectedResult) { PinningData pinningData{ PinningData::Disposition::ReadOnly }; + auto availableVersions = GetAvailableVersionsForInstalledVersion(package); for (const auto& entry : expectedResult.ResultsForPinBehavior) { auto pinBehavior = entry.first; const auto& result = entry.second; - auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, package->GetInstalledVersion()); - auto latestAvailable = evaluator.GetLatestAvailableVersionForPins(package); + auto evaluator = pinningData.CreatePinStateEvaluator(pinBehavior, GetInstalledVersion(package)); + auto latestAvailable = evaluator.GetLatestAvailableVersionForPins(availableVersions); REQUIRE(evaluator.IsUpdate(latestAvailable) == result.IsUpdateAvailable); @@ -1096,13 +1130,13 @@ void RequireExpectedResultsWithPin(std::shared_ptr package, const Expe } } - auto availableVersionKeys = package->GetAvailableVersionKeys(); + auto availableVersionKeys = availableVersions->GetVersionKeys(); REQUIRE(availableVersionKeys.size() == expectedResult.AvailableVersions.size()); for (size_t i = 0; i < availableVersionKeys.size(); ++i) { - auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, package->GetInstalledVersion()); + auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, GetInstalledVersion(package)); - auto packageVersion = package->GetAvailableVersion(expectedResult.AvailableVersions[i]); + auto packageVersion = availableVersions->GetVersion(expectedResult.AvailableVersions[i]); REQUIRE(packageVersion); REQUIRE(availableVersionKeys[i].SourceId == expectedResult.AvailableVersions[i].SourceId); REQUIRE(availableVersionKeys[i].Version == expectedResult.AvailableVersions[i].Version); @@ -1122,7 +1156,7 @@ TEST_CASE("CompositeSource_Pinning_AvailableVersionPinned", "[CompositeSource][P TestUserSettings userSettings; CompositeTestSetup setup; - auto installedPackage = TestPackage::Make(MakeDefaultManifest("1.0.1"sv), TestPackage::MetadataMap{}); + auto installedPackage = TestCompositePackage::Make(MakeDefaultManifest("1.0.1"sv), TestCompositePackage::MetadataMap{}); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) @@ -1130,7 +1164,7 @@ TEST_CASE("CompositeSource_Pinning_AvailableVersionPinned", "[CompositeSource][P auto manifest1 = MakeDefaultManifest("1.0.0"sv); auto manifest2 = MakeDefaultManifest("1.0.1"sv); auto manifest3 = MakeDefaultManifest("1.1.0"sv); - auto package = TestPackage::Make( + auto package = TestCompositePackage::Make( std::vector{ manifest3, manifest2, manifest1 }, setup.Available); @@ -1233,12 +1267,12 @@ TEST_CASE("CompositeSource_Pinning_OneSourcePinned", "[CompositeSource][PinFlow] TestUserSettings userSettings; CompositeTestSetup setup; - auto installedPackage = TestPackage::Make(MakeDefaultManifest("1.0"sv), TestPackage::MetadataMap{}); + auto installedPackage = TestCompositePackage::Make(MakeDefaultManifest("1.0"sv), TestCompositePackage::MetadataMap{}); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { - auto package = TestPackage::Make(std::vector{ MakeDefaultManifest("2.0"sv) }, setup.Available); + auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("2.0"sv) }, setup.Available); SearchResult result; result.Matches.emplace_back(package, Criteria()); @@ -1249,7 +1283,7 @@ TEST_CASE("CompositeSource_Pinning_OneSourcePinned", "[CompositeSource][PinFlow] setup.Composite.AddAvailableSource(Source{ secondAvailable }); secondAvailable->SearchFunction = [&](const SearchRequest&) { - auto package = TestPackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); + auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); SearchResult result; result.Matches.emplace_back(package, Criteria()); @@ -1289,12 +1323,12 @@ TEST_CASE("CompositeSource_Pinning_OneSourceGated", "[CompositeSource][PinFlow]" TestUserSettings userSettings; CompositeTestSetup setup; - auto installedPackage = TestPackage::Make(MakeDefaultManifest("1.0"sv), TestPackage::MetadataMap{}); + auto installedPackage = TestCompositePackage::Make(MakeDefaultManifest("1.0"sv), TestCompositePackage::MetadataMap{}); setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); setup.Available->SearchFunction = [&](const SearchRequest&) { - auto package = TestPackage::Make( + auto package = TestCompositePackage::Make( std::vector{ MakeDefaultManifest("2.0"sv), MakeDefaultManifest("1.2"sv), @@ -1310,7 +1344,7 @@ TEST_CASE("CompositeSource_Pinning_OneSourceGated", "[CompositeSource][PinFlow]" setup.Composite.AddAvailableSource(Source{ secondAvailable }); secondAvailable->SearchFunction = [&](const SearchRequest&) { - auto package = TestPackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); + auto package = TestCompositePackage::Make(std::vector{ MakeDefaultManifest("1.1"sv) }, secondAvailable); SearchResult result; result.Matches.emplace_back(package, Criteria()); diff --git a/src/AppInstallerCLITests/DependenciesTestSource.h b/src/AppInstallerCLITests/DependenciesTestSource.h index ae36b03c6e..250c8bf34b 100644 --- a/src/AppInstallerCLITests/DependenciesTestSource.h +++ b/src/AppInstallerCLITests/DependenciesTestSource.h @@ -188,9 +188,9 @@ namespace TestCommon //auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest }, shared_from_this() ), @@ -200,7 +200,7 @@ namespace TestCommon { result.Matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( std::vector{ manifest }, shared_from_this() ), diff --git a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp index 7c26bf0615..520304d779 100644 --- a/src/AppInstallerCLITests/PackageTrackingCatalog.cpp +++ b/src/AppInstallerCLITests/PackageTrackingCatalog.cpp @@ -86,8 +86,9 @@ TEST_CASE("TrackingCatalog_Install", "[tracking_catalog]") SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); + REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); - auto trackingVersion = resultAfter.Matches[0].Package->GetLatestAvailableVersion(); + auto trackingVersion = resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(trackingVersion); auto metadata = trackingVersion->GetMetadata(); @@ -113,7 +114,8 @@ TEST_CASE("TrackingCatalog_Reinstall", "[tracking_catalog]") SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name) == + REQUIRE(resultBefore.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(resultBefore.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Name) == manifest.DefaultLocalization.Get()); // Change name @@ -124,7 +126,8 @@ TEST_CASE("TrackingCatalog_Reinstall", "[tracking_catalog]") SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Name) == + REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Name) == newName); } @@ -147,17 +150,19 @@ TEST_CASE("TrackingCatalog_Upgrade", "[tracking_catalog]") SearchResult resultBefore = catalog.Search(request); REQUIRE(resultBefore.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version) == + REQUIRE(resultBefore.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(resultBefore.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); - // Change name + // Change version manifest.Version = "99.1.2.3"; catalog.RecordInstall(manifest, manifest.Installers[0], true); SearchResult resultAfter = catalog.Search(request); REQUIRE(resultAfter.Matches.size() == 1); - REQUIRE(resultBefore.Matches[0].Package->GetLatestAvailableVersion()->GetProperty(PackageVersionProperty::Version) == + REQUIRE(resultAfter.Matches[0].Package->GetAvailable().size() == 1); + REQUIRE(resultAfter.Matches[0].Package->GetAvailable()[0]->GetLatestVersion()->GetProperty(PackageVersionProperty::Version) == manifest.Version); } diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp index f30b80c654..c199a54f5a 100644 --- a/src/AppInstallerCLITests/SQLiteIndexSource.cpp +++ b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -86,7 +86,7 @@ TEST_CASE("SQLiteIndexSource_Id", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); - auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); + auto latestVersion = results.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Id).get() == manifest.Id); } @@ -107,7 +107,7 @@ TEST_CASE("SQLiteIndexSource_Name", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); - auto latestVersion = results.Matches[0].Package->GetLatestAvailableVersion(); + auto latestVersion = results.Matches[0].Package->GetAvailable()[0]->GetLatestVersion(); REQUIRE(latestVersion->GetProperty(PackageVersionProperty::Name).get() == manifest.DefaultLocalization.Get()); } @@ -128,8 +128,9 @@ TEST_CASE("SQLiteIndexSource_Versions", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); + REQUIRE(results.Matches[0].Package->GetAvailable().size() == 1); - auto result = results.Matches[0].Package->GetAvailableVersionKeys(); + auto result = results.Matches[0].Package->GetAvailable()[0]->GetVersionKeys(); REQUIRE(result.size() == 1); REQUIRE(result[0].Version == manifest.Version); REQUIRE(result[0].Channel == manifest.Channel); @@ -151,9 +152,10 @@ TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") auto results = source->Search(request); REQUIRE(results.Matches.size() == 1); REQUIRE(results.Matches[0].Package); - auto package = results.Matches[0].Package.get(); + REQUIRE(results.Matches[0].Package->GetAvailable().size() == 1); + auto package = results.Matches[0].Package->GetAvailable()[0]; - auto specificResultVersion = package->GetAvailableVersion(PackageVersionKey("", manifest.Version, manifest.Channel)); + auto specificResultVersion = package->GetVersion(PackageVersionKey("", manifest.Version, manifest.Channel)); REQUIRE(specificResultVersion); auto specificResult = specificResultVersion->GetManifest(); REQUIRE(specificResult.Id == manifest.Id); @@ -161,7 +163,7 @@ TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") REQUIRE(specificResult.Version == manifest.Version); REQUIRE(specificResult.Channel == manifest.Channel); - auto latestResultVersion = package->GetAvailableVersion(PackageVersionKey("", "", manifest.Channel)); + auto latestResultVersion = package->GetVersion(PackageVersionKey("", "", manifest.Channel)); REQUIRE(latestResultVersion); auto latestResult = latestResultVersion->GetManifest(); REQUIRE(latestResult.Id == manifest.Id); @@ -169,7 +171,7 @@ TEST_CASE("SQLiteIndexSource_GetManifest", "[sqliteindexsource]") REQUIRE(latestResult.Version == manifest.Version); REQUIRE(latestResult.Channel == manifest.Channel); - auto noResultVersion = package->GetAvailableVersion(PackageVersionKey("", "blargle", "flargle")); + auto noResultVersion = package->GetVersion(PackageVersionKey("", "blargle", "flargle")); REQUIRE(!noResultVersion); } @@ -188,9 +190,11 @@ TEST_CASE("SQLiteIndexSource_IsSame", "[sqliteindexsource]") auto result1 = source->Search(request); REQUIRE(result1.Matches.size() == 1); + REQUIRE(result1.Matches[0].Package->GetAvailable().size() == 1); auto result2 = source->Search(request); REQUIRE(result2.Matches.size() == 1); + REQUIRE(result2.Matches[0].Package->GetAvailable().size() == 1); - REQUIRE(result1.Matches[0].Package->IsSame(result2.Matches[0].Package.get())); + REQUIRE(result1.Matches[0].Package->GetAvailable()[0]->IsSame(result2.Matches[0].Package->GetAvailable()[0].get())); } diff --git a/src/AppInstallerCLITests/Sources.cpp b/src/AppInstallerCLITests/Sources.cpp index dc8401ccfd..b19c53102f 100644 --- a/src/AppInstallerCLITests/Sources.cpp +++ b/src/AppInstallerCLITests/Sources.cpp @@ -195,9 +195,9 @@ namespace PackageMatchFilter testMatchFilter1{ PackageMatchField::Id, MatchType::Exact, "test" }; PackageMatchFilter testMatchFilter2{ PackageMatchField::Name, MatchType::Exact, "test" }; PackageMatchFilter testMatchFilter3{ PackageMatchField::Id, MatchType::CaseInsensitive, "test" }; - result.Matches.emplace_back(std::shared_ptr(), testMatchFilter1); - result.Matches.emplace_back(std::shared_ptr(), testMatchFilter2); - result.Matches.emplace_back(std::shared_ptr(), testMatchFilter3); + result.Matches.emplace_back(nullptr, testMatchFilter1); + result.Matches.emplace_back(nullptr, testMatchFilter2); + result.Matches.emplace_back(nullptr, testMatchFilter3); return result; } }; diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index 73130ce0df..06050b6402 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -117,34 +117,28 @@ namespace TestCommon } } - TestPackage::TestPackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) + TestPackage::TestPackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) : + Source(source) { for (const auto& manifest : available) { - AvailableVersions.emplace_back(TestPackageVersion::Make(manifest, source, hideSystemReferenceStringsOnVersion)); + Versions.emplace_back(TestPackageVersion::Make(manifest, source, hideSystemReferenceStringsOnVersion)); } } - TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : - InstalledVersion(TestPackageVersion::Make(installed, std::move(installationMetadata), source)) + TestPackage::TestPackage(const Manifest& installed, MetadataMap installationMetadata, std::weak_ptr source) : + Source(source) { - for (const auto& manifest : available) - { - AvailableVersions.emplace_back(TestPackageVersion::Make(manifest, source)); - } + Versions.emplace_back(TestPackageVersion::Make(installed, std::move(installationMetadata), source)); } TestPackage::LocIndString TestPackage::GetProperty(PackageProperty property) const { std::shared_ptr truth; - if (!AvailableVersions.empty()) + if (!Versions.empty()) { - truth = AvailableVersions[0]; - } - else - { - truth = InstalledVersion; + truth = Versions[0]; } if (!truth) @@ -163,34 +157,29 @@ namespace TestCommon } } - std::shared_ptr TestPackage::GetInstalledVersion() const - { - return InstalledVersion; - } - - std::vector TestPackage::GetAvailableVersionKeys() const + std::vector TestPackage::GetVersionKeys() const { std::vector result; - for (const auto& version : AvailableVersions) + for (const auto& version : Versions) { result.emplace_back(PackageVersionKey(version->GetSource().GetIdentifier(), version->GetProperty(PackageVersionProperty::Version).get(), version->GetProperty(PackageVersionProperty::Channel).get())); } return result; } - std::shared_ptr TestPackage::GetLatestAvailableVersion() const + std::shared_ptr TestPackage::GetLatestVersion() const { - if (AvailableVersions.empty()) + if (Versions.empty()) { return {}; } - return AvailableVersions[0]; + return Versions[0]; } - std::shared_ptr TestPackage::GetAvailableVersion(const PackageVersionKey& versionKey) const + std::shared_ptr TestPackage::GetVersion(const PackageVersionKey& versionKey) const { - for (const auto& version : AvailableVersions) + for (const auto& version : Versions) { if ((versionKey.Version.empty() || versionKey.Version == version->GetProperty(PackageVersionProperty::Version).get()) && (versionKey.Channel.empty() || versionKey.Channel == version->GetProperty(PackageVersionProperty::Channel).get())) @@ -202,6 +191,11 @@ namespace TestCommon return {}; } + Repository::Source TestPackage::GetSource() const + { + return std::const_pointer_cast(Source.lock()); + } + bool TestPackage::IsSame(const IPackage* other) const { if (IsSameOverride) @@ -212,15 +206,14 @@ namespace TestCommon const TestPackage* otherAvailable = PackageCast(other); if (!otherAvailable || - InstalledVersion.get() != otherAvailable->InstalledVersion.get() || - AvailableVersions.size() != otherAvailable->AvailableVersions.size()) + Versions.size() != otherAvailable->Versions.size()) { return false; } - for (size_t i = 0; i < AvailableVersions.size(); ++i) + for (size_t i = 0; i < Versions.size(); ++i) { - if (AvailableVersions[i].get() != otherAvailable->AvailableVersions[i].get()) + if (Versions[i].get() != otherAvailable->Versions[i].get()) { return false; } @@ -239,6 +232,62 @@ namespace TestCommon return nullptr; } + TestCompositePackage::TestCompositePackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) + { + if (!available.empty()) + { + Available.emplace_back(TestPackage::Make(available, source, hideSystemReferenceStringsOnVersion)); + } + } + + TestCompositePackage::TestCompositePackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available, std::weak_ptr source) : + Installed(TestPackage::Make(installed, std::move(installationMetadata), source)) + { + if (!available.empty()) + { + Available.emplace_back(TestPackage::Make(available, source)); + } + } + + TestCompositePackage::LocIndString TestCompositePackage::GetProperty(PackageProperty property) const + { + std::shared_ptr truth; + + if (!Available.empty()) + { + truth = Available[0]; + } + else + { + truth = Installed; + } + + if (!truth) + { + THROW_HR(E_NOT_VALID_STATE); + } + + switch (property) + { + case PackageProperty::Id: + return truth->GetProperty(PackageProperty::Id); + case PackageProperty::Name: + return truth->GetProperty(PackageProperty::Name); + default: + return {}; + } + } + + std::shared_ptr TestCompositePackage::GetInstalled() + { + return Installed; + } + + std::vector> TestCompositePackage::GetAvailable() + { + return { Available.begin(), Available.end() }; + } + const SourceDetails& TestSource::GetDetails() const { return Details; diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index 33aa207ec6..f86789f0ab 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -56,7 +56,7 @@ namespace TestCommon TestPackage(const std::vector& available, std::weak_ptr source = {}, bool hideSystemReferenceStringsOnVersion = false); // Create a package with an installed version, metadata, and optionally available versions. - TestPackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); + TestPackage(const Manifest& installed, MetadataMap installationMetadata, std::weak_ptr source = {}); template static std::shared_ptr Make(Args&&... args) @@ -65,18 +65,46 @@ namespace TestCommon } AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; - std::shared_ptr GetInstalledVersion() const override; - std::vector GetAvailableVersionKeys() const override; - std::shared_ptr GetLatestAvailableVersion() const override; - std::shared_ptr GetAvailableVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; + std::vector GetVersionKeys() const override; + std::shared_ptr GetLatestVersion() const override; + std::shared_ptr GetVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; + AppInstaller::Repository::Source GetSource() const override; bool IsSame(const IPackage* other) const override; const void* CastTo(AppInstaller::Repository::IPackageType type) const override; - std::shared_ptr InstalledVersion; - std::vector> AvailableVersions; + std::vector> Versions; + std::weak_ptr Source; std::function IsSameOverride; }; + // ICompositePackage for TestSource + struct TestCompositePackage : public AppInstaller::Repository::ICompositePackage + { + using Manifest = AppInstaller::Manifest::Manifest; + using ISource = AppInstaller::Repository::ISource; + using LocIndString = AppInstaller::Utility::LocIndString; + using MetadataMap = TestPackageVersion::MetadataMap; + + // Create a package with only available versions using these manifests. + TestCompositePackage(const std::vector& available, std::weak_ptr source = {}, bool hideSystemReferenceStringsOnVersion = false); + + // Create a package with an installed version, metadata, and optionally available versions. + TestCompositePackage(const Manifest& installed, MetadataMap installationMetadata, const std::vector& available = {}, std::weak_ptr source = {}); + + template + static std::shared_ptr Make(Args&&... args) + { + return std::make_shared(std::forward(args)...); + } + + AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; + std::shared_ptr GetInstalled() override; + std::vector> GetAvailable() override; + + std::shared_ptr Installed; + std::vector> Available; + }; + // An ISource implementation for use across the test code. struct TestSource : public AppInstaller::Repository::ISource, public std::enable_shared_from_this { diff --git a/src/AppInstallerCLITests/WorkflowCommon.cpp b/src/AppInstallerCLITests/WorkflowCommon.cpp index 3c35a3c104..7422808557 100644 --- a/src/AppInstallerCLITests/WorkflowCommon.cpp +++ b/src/AppInstallerCLITests/WorkflowCommon.cpp @@ -27,7 +27,7 @@ namespace TestCommon auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest }, source), + TestCompositePackage::Make(std::vector{ manifest }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnOne"))); }); @@ -37,13 +37,13 @@ namespace TestCommon auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest }, source), + TestCompositePackage::Make(std::vector{ manifest }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); auto manifest2 = YamlParser::CreateFromPath(TestDataFile("Manifest-Good.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make(std::vector{ manifest2 }, source), + TestCompositePackage::Make(std::vector{ manifest2 }, source), PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); }); @@ -55,9 +55,9 @@ namespace TestCommon auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2.yaml")); auto testPackage = - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, @@ -66,7 +66,10 @@ namespace TestCommon std::vector{ manifest3, manifest2, manifest }, source ); - testPackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; + for (auto& availablePackage : testPackage->Available) + { + availablePackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; + } matches.emplace_back( ResultMatch( testPackage, @@ -81,9 +84,9 @@ namespace TestCommon auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2_LicenseAgreement.yaml")); auto testPackage = - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, @@ -92,7 +95,10 @@ namespace TestCommon std::vector{ manifest3, manifest2, manifest }, source ); - testPackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; + for (auto& availablePackage : testPackage->Available) + { + availablePackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; + } matches.emplace_back( ResultMatch( testPackage, @@ -106,9 +112,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Portable.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Portable" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Portable" } }, std::vector{ manifest2, manifest }, source ), @@ -123,9 +129,9 @@ namespace TestCommon matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), @@ -140,9 +146,9 @@ namespace TestCommon matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), @@ -157,9 +163,9 @@ namespace TestCommon matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Msix" }, { PackageVersionMetadata::PinnedState, "PinnedByManifest" }, @@ -177,9 +183,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Zip_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), @@ -195,9 +201,9 @@ namespace TestCommon installed.Version = "1.0.0.0"; matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( installed, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "MSStore" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "MSStore" } }, std::vector{ available }, source ), @@ -211,9 +217,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_ExpectedReturnCodes.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), @@ -229,9 +235,9 @@ namespace TestCommon installed.Version = "unknown"; matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( installed, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ available }, source ), @@ -245,9 +251,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest2, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), @@ -261,9 +267,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), @@ -277,9 +283,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_ARPInstallerType.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Msix" } }, std::vector{ manifest2, manifest }, source ), @@ -293,9 +299,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_UnsupportedArgs.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), @@ -308,7 +314,7 @@ namespace TestCommon auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( std::vector{ manifest }, source ), @@ -322,9 +328,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_ExeDependencies.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, @@ -342,7 +348,7 @@ namespace TestCommon auto manifest = YamlParser::CreateFromPath(TestDataFile("Installer_Msix_WFDependency.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( std::vector{ manifest }, source ), @@ -356,9 +362,9 @@ namespace TestCommon auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2_LicenseAgreement.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, + TestCompositePackage::MetadataMap{ { PackageVersionMetadata::InstalledType, "Exe" } }, std::vector{ manifest2, manifest }, source ), @@ -373,9 +379,9 @@ namespace TestCommon auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2.yaml")); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, @@ -387,9 +393,9 @@ namespace TestCommon PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); matches.emplace_back( ResultMatch( - TestPackage::Make( + TestCompositePackage::Make( manifest2, - TestPackage::MetadataMap + TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, { PackageVersionMetadata::StandardUninstallCommand, "C:\\uninstall.exe" }, diff --git a/src/AppInstallerRepositoryCore/ARPCorrelation.cpp b/src/AppInstallerRepositoryCore/ARPCorrelation.cpp index 862537fff0..1e8fe8eb49 100644 --- a/src/AppInstallerRepositoryCore/ARPCorrelation.cpp +++ b/src/AppInstallerRepositoryCore/ARPCorrelation.cpp @@ -89,7 +89,7 @@ namespace AppInstaller::Repository::Correlation auto score = algorithm.ComputeConfidence(arpEntry); AICLI_LOG(Repo, Verbose, << "Match confidence for " << arpEntry.Entry->GetProperty(PackageProperty::Id) << ": " << score); - result.Measures.emplace_back(CorrelationMeasure{ score, arpEntry.Entry->GetInstalledVersion() }); + result.Measures.emplace_back(CorrelationMeasure{ score, arpEntry.Entry->GetLatestVersion() }); } std::sort(result.Measures.begin(), result.Measures.end(), [](const CorrelationMeasure& a, const CorrelationMeasure& b) { return a.Measure > b.Measure; }); @@ -122,11 +122,11 @@ namespace AppInstaller::Repository::Correlation for (const auto& entry : preInstallARP.Search({}).Matches) { - auto installed = entry.Package->GetInstalledVersion(); + auto installed = entry.Package->GetInstalled()->GetLatestVersion(); if (installed) { m_preInstallSnapshot.emplace_back(std::make_tuple( - entry.Package->GetProperty(PackageProperty::Id), + installed->GetProperty(PackageVersionProperty::Id), installed->GetProperty(PackageVersionProperty::Version), installed->GetProperty(PackageVersionProperty::Channel))); } @@ -143,17 +143,17 @@ namespace AppInstaller::Repository::Correlation for (auto& entry : m_postInstallSnapshotSource.Search({}).Matches) { - auto installed = entry.Package->GetInstalledVersion(); + auto installed = entry.Package->GetInstalled()->GetLatestVersion(); if (installed) { auto entryKey = std::make_tuple( - entry.Package->GetProperty(PackageProperty::Id), + installed->GetProperty(PackageVersionProperty::Id), installed->GetProperty(PackageVersionProperty::Version), installed->GetProperty(PackageVersionProperty::Channel)); auto itr = std::lower_bound(m_preInstallSnapshot.begin(), m_preInstallSnapshot.end(), entryKey); - m_postInstallSnapshot.emplace_back(entry.Package, itr == m_preInstallSnapshot.end() || *itr != entryKey); + m_postInstallSnapshot.emplace_back(entry.Package->GetInstalled(), itr == m_preInstallSnapshot.end() || *itr != entryKey); } } } @@ -239,7 +239,7 @@ namespace AppInstaller::Repository::Correlation { for (const auto& byManifest : findByManifest.Matches) { - if (change.Entry->IsSame(byManifest.Package.get())) + if (change.Entry->IsSame(byManifest.Package->GetInstalled().get())) { packagesInBoth.emplace_back(change.Entry); break; @@ -266,18 +266,18 @@ namespace AppInstaller::Repository::Correlation // If there is only a single common package (changed and matches), it is almost certainly the correct one. if (settings.AllowNormalization && packagesInBoth.size() == 1) { - result.Package = packagesInBoth[0]->GetInstalledVersion(); + result.Package = packagesInBoth[0]->GetLatestVersion(); result.Reason = "normalization match and new/changed"; } // If it wasn't changed but we still find a match, that is the best thing to report. else if (settings.AllowNormalization && findByManifest.Matches.size() == 1) { - result.Package = findByManifest.Matches[0].Package->GetInstalledVersion(); + result.Package = findByManifest.Matches[0].Package->GetInstalled()->GetLatestVersion(); result.Reason = "normalization match (not new/changed)"; } else if (settings.AllowSingleChange && result.ChangesToARP == 1) { - result.Package = std::find_if(m_postInstallSnapshot.begin(), m_postInstallSnapshot.end(), [](const ARPEntry& e) { return e.IsNewOrUpdated; })->Entry->GetInstalledVersion(); + result.Package = std::find_if(m_postInstallSnapshot.begin(), m_postInstallSnapshot.end(), [](const ARPEntry& e) { return e.IsNewOrUpdated; })->Entry->GetLatestVersion(); result.Reason = "only new/changed value"; } else diff --git a/src/AppInstallerRepositoryCore/ARPCorrelationAlgorithms.cpp b/src/AppInstallerRepositoryCore/ARPCorrelationAlgorithms.cpp index 3692d2e7ca..0f5bfd548b 100644 --- a/src/AppInstallerRepositoryCore/ARPCorrelationAlgorithms.cpp +++ b/src/AppInstallerRepositoryCore/ARPCorrelationAlgorithms.cpp @@ -145,8 +145,8 @@ namespace AppInstaller::Repository::Correlation { // Name and Publisher are available as multi properties, but for ARP entries there will only be 0 or 1 values. NameAndPublisher arpNameAndPublisher( - NormalizeAndPrepareName(arpEntry.Entry->GetInstalledVersion()->GetProperty(PackageVersionProperty::Name).get()), - NormalizeAndPreparePublisher(arpEntry.Entry->GetInstalledVersion()->GetProperty(PackageVersionProperty::Publisher).get())); + NormalizeAndPrepareName(arpEntry.Entry->GetLatestVersion()->GetProperty(PackageVersionProperty::Name).get()), + NormalizeAndPreparePublisher(arpEntry.Entry->GetLatestVersion()->GetProperty(PackageVersionProperty::Publisher).get())); // Get the best score across all localizations double bestMatchingScore = 0; diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 79d1a98a1b..bf7dfab083 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -406,10 +406,12 @@ + + @@ -493,6 +495,7 @@ + Create diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 1054476c89..7512c9118c 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -321,9 +321,6 @@ Microsoft\Schema\Portable_1_0 - - Microsoft - Microsoft\Schema @@ -381,9 +378,6 @@ Microsoft\Schema - - Microsoft - Microsoft\Schema\Checkpoint_1_0 @@ -405,6 +399,21 @@ Rest\Schema + + Public\winget + + + Public\winget + + + Public\winget + + + Public\winget + + + Public\winget + Rest\Schema\1_7\Json @@ -638,6 +647,12 @@ Rest\Schema + + Source Files + + + Source Files + Rest\Schema\1_7\Json diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 2a6c9dd0d7..8ce367e9a9 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -34,10 +34,22 @@ namespace AppInstaller::Repository return false; } + // Gets the only available package from the composite, ensuring this fact in test contexts. + std::shared_ptr OnlyAvailable(const std::shared_ptr& composite) + { + std::vector> availablePackages = composite->GetAvailable(); + +#ifndef AICLI_DISABLE_TEST_HOOKS + THROW_HR_IF(E_UNEXPECTED, availablePackages.size() != 1); +#endif + + return std::move(availablePackages.front()); + } + // Move returns if there is only one package in the matches that is strong; otherwise returns an empty value. - std::shared_ptr FindOnlyStrongMatchFieldResult(std::vector& matches) + std::shared_ptr FindOnlyStrongMatchFieldResult(std::vector& matches) { - std::shared_ptr result; + std::shared_ptr result; for (auto&& match : matches) { @@ -63,7 +75,7 @@ namespace AppInstaller::Repository // Gets a single matching package from the results template - std::shared_ptr GetMatchingPackage(std::vector& matches, MultipleIntro&& multipleIntro, Indeterminate&& indeterminate) + std::shared_ptr GetMatchingPackage(std::vector& matches, MultipleIntro&& multipleIntro, Indeterminate&& indeterminate) { if (matches.empty()) { @@ -91,14 +103,15 @@ namespace AppInstaller::Repository // For a given package from a tracking catalog, get the latest write time and its corresponding package version. // Look at all versions rather than just the latest to account for the potential of downgrading. std::pair> GetLatestTrackingWriteTimeAndPackageVersion( - const std::shared_ptr& trackingPackage) + const std::shared_ptr& trackingCompositePackage) { + std::shared_ptr trackingPackage = OnlyAvailable(trackingCompositePackage); std::chrono::system_clock::time_point resultTime{}; std::shared_ptr resultVersion; - for (const auto& key : trackingPackage->GetAvailableVersionKeys()) + for (const auto& key : trackingPackage->GetVersionKeys()) { - auto version = trackingPackage->GetAvailableVersion(key); + auto version = trackingPackage->GetVersion(key); if (version) { auto metadata = version->GetMetadata(); @@ -136,12 +149,12 @@ namespace AppInstaller::Repository { // Stores raw versions value strings to run a preliminary check whether version mapping is needed. std::vector> rawVersionValues; - auto versionKeys = availablePackage->GetAvailableVersionKeys(); + auto versionKeys = availablePackage->GetVersionKeys(); bool shouldTryPerformMapping = false; for (auto const& versionKey : versionKeys) { - auto availableVersion = availablePackage->GetAvailableVersion(versionKey); + auto availableVersion = availablePackage->GetVersion(versionKey); std::string arpMinVersion = availableVersion->GetProperty(PackageVersionProperty::ArpMinVersion); std::string arpMaxVersion = availableVersion->GetProperty(PackageVersionProperty::ArpMaxVersion); @@ -327,163 +340,128 @@ namespace AppInstaller::Repository std::shared_ptr m_trackingPackageVersion; }; - // A composite package for the CompositeSource. - struct CompositePackage : public IPackage + // An IPackage for the installed package of a CompositePackage. + // Supports only a single version of a single package at this time. + struct CompositeInstalledPackage : public IPackage { - static constexpr IPackageType PackageType = IPackageType::CompositePackage; + static constexpr IPackageType PackageType = IPackageType::CompositeInstalledPackage; - CompositePackage(std::shared_ptr installedPackage, std::shared_ptr availablePackage = {}) - { - // Grab the installed version's channel to allow for filtering in calls to get available info. - if (installedPackage) - { - m_installedPackage = std::move(installedPackage); - auto installedVersion = m_installedPackage->GetInstalledVersion(); - if (installedVersion) - { - m_installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); - } - } + CompositeInstalledPackage(std::shared_ptr package, Source trackingSource, std::shared_ptr trackingPackageVersion, std::string overrideVersion = {}) : + m_package(std::move(package)), m_trackingSource(std::move(trackingSource)), m_trackingPackageVersion(std::move(trackingPackageVersion)), m_overrideVersion(std::move(overrideVersion)) + {} - AddAvailablePackage(std::move(availablePackage)); + Utility::LocIndString GetProperty(PackageProperty property) const override + { + return m_package->GetProperty(property); } - Utility::LocIndString GetProperty(PackageProperty property) const override + std::vector GetVersionKeys() const override { - std::shared_ptr truth; - if (m_defaultAvailablePackage) - { - truth = m_defaultAvailablePackage->GetLatestAvailableVersion(); - } - if (!truth) - { - truth = m_trackingPackageVersion; - } - if (!truth) - { - truth = GetInstalledVersion(); - } - if (!truth) + auto result = m_package->GetVersionKeys(); + THROW_HR_IF(E_UNEXPECTED, result.size() != 1); + if (!m_overrideVersion.empty()) { - truth = GetLatestAvailableVersion(); + result.front().Version = m_overrideVersion; } + return result; + } - switch (property) - { - case PackageProperty::Id: - return truth->GetProperty(PackageVersionProperty::Id); - case PackageProperty::Name: - return truth->GetProperty(PackageVersionProperty::Name); - default: - THROW_HR(E_UNEXPECTED); - } + std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const + { + return (GetVersionKeys().front().IsMatch(versionKey)) ? GetLatestVersion() : std::shared_ptr{}; } - std::shared_ptr GetInstalledVersion() const override + std::shared_ptr GetLatestVersion() const override { - if (m_installedPackage) + return std::make_shared(m_package->GetLatestVersion(), m_trackingSource, m_trackingPackageVersion, m_overrideVersion); + } + + Source GetSource() const override + { + // If there is a tracking source, use it instead to indicate that it came from there. + if (m_trackingSource) { - auto installedVersion = m_installedPackage->GetInstalledVersion(); - if (installedVersion) - { - return std::make_shared(std::move(installedVersion), m_trackingSource, m_trackingPackageVersion, m_overrideInstalledVersion); - } + return m_trackingSource; } - return {}; + return m_package->GetSource(); } - std::vector GetAvailableVersionKeys() const override + bool IsSame(const IPackage* other) const override { - std::vector result; + const CompositeInstalledPackage* otherPackage = PackageCast(other); - for (const auto& availablePackage : m_availablePackages) + if (otherPackage) { - auto versionKeys = availablePackage->GetAvailableVersionKeys(); - std::copy(versionKeys.begin(), versionKeys.end(), std::back_inserter(result)); + return m_package->IsSame(otherPackage->m_package.get()); } - // Remove all elements whose channel does not match the installed package. - std::string_view channel = m_installedChannel; - result.erase( - std::remove_if(result.begin(), result.end(), [&](const PackageVersionKey& pvk) { return !Utility::ICUCaseInsensitiveEquals(pvk.Channel, channel); }), - result.end()); - - // Put latest versions at the front; for versions available from multiple sources maintain the order they were added in - std::stable_sort(result.begin(), result.end()); - - return result; + return false; } - std::shared_ptr GetLatestAvailableVersion() const override + const void* CastTo(IPackageType type) const override { - return GetAvailableVersion({ "", "", m_installedChannel.get() }); + if (type == PackageType) + { + return this; + } + + return nullptr; } - std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const override + private: + std::shared_ptr m_package; + Source m_trackingSource; + std::shared_ptr m_trackingPackageVersion; + std::string m_overrideVersion; + }; + + // An ICompositePackage for the CompositeSource. + struct CompositePackage : public ICompositePackage + { + // The availablePackage may only contain one available package within it, as it is expected to be the output of a search on a single source. + CompositePackage(const std::shared_ptr& installedPackage, const std::shared_ptr& availablePackage = {}) { - for (const auto& availablePackage : m_availablePackages) + if (installedPackage) { - if (!Utility::IsEmptyOrWhitespace(versionKey.SourceId)) - { - auto latestAvailable = availablePackage->GetLatestAvailableVersion(); - if (latestAvailable && versionKey.SourceId != latestAvailable->GetSource().GetIdentifier()) - { - continue; - } - } - - return availablePackage->GetAvailableVersion(versionKey); + m_installedPackage = installedPackage->GetInstalled(); } - return {}; + AddAvailablePackage(availablePackage); } - bool IsSame(const IPackage* other) const override + Utility::LocIndString GetProperty(PackageProperty property) const override { - const CompositePackage* otherComposite = PackageCast(other); - - if (!otherComposite || - static_cast(m_installedPackage) != static_cast(otherComposite->m_installedPackage) || - m_availablePackages.size() != otherComposite->m_availablePackages.size()) + std::shared_ptr truth; + if (m_defaultAvailablePackage) { - return false; + truth = m_defaultAvailablePackage; } - - if (m_installedPackage && !m_installedPackage->IsSame(otherComposite->m_installedPackage.get())) + if (!truth) { - return false; + truth = m_trackingPackage; } - - for (const auto& availablePackage : m_availablePackages) + if (!truth) { - bool foundMatch = false; - for (const auto& otherAvailablePackage : otherComposite->m_availablePackages) - { - if (availablePackage->IsSame(otherAvailablePackage.get())) - { - foundMatch = true; - break; - } - } - - if (!foundMatch) - { - return false; - } + truth = m_installedPackage; } - return true; + return truth->GetProperty(property); } - const void* CastTo(IPackageType type) const override + std::shared_ptr GetInstalled() override { - if (type == PackageType) + if (m_installedPackage) { - return this; + return std::make_shared(m_installedPackage, m_trackingSource, m_trackingPackageVersion, m_overrideInstalledVersion); } - return nullptr; + return {}; + } + + std::vector> GetAvailable() override + { + return m_availablePackages; } bool IsSameAsAnyAvailable(const IPackage* other) const @@ -504,14 +482,7 @@ namespace AppInstaller::Repository std::shared_ptr GetInstalledPackage() const { - if (m_installedPackage) - { - return m_installedPackage; - } - else - { - return {}; - } + return m_installedPackage; } const std::shared_ptr& GetTrackingPackage() const @@ -519,18 +490,20 @@ namespace AppInstaller::Repository return m_trackingPackage; } - void AddAvailablePackage(std::shared_ptr availablePackage) + void AddAvailablePackage(const std::shared_ptr& availablePackage) { if (availablePackage) { + std::shared_ptr singlePackage = OnlyAvailable(availablePackage); + if (!m_defaultAvailablePackage) { // Set override only with the first available version found - m_defaultAvailablePackage = availablePackage; + m_defaultAvailablePackage = singlePackage; TrySetOverrideInstalledVersion(m_defaultAvailablePackage); } - m_availablePackages.emplace_back(std::move(availablePackage)); + m_availablePackages.emplace_back(std::move(singlePackage)); } } @@ -541,13 +514,18 @@ namespace AppInstaller::Repository m_trackingPackageVersion = std::move(trackingPackageVersion); } + const Source& GetTrackingSource() const + { + return m_trackingSource; + } + private: // Try to set a version that will override the version string from the installed package void TrySetOverrideInstalledVersion(std::shared_ptr availablePackage) { if (m_installedPackage && availablePackage) { - auto installedVersion = m_installedPackage->GetInstalledVersion(); + auto installedVersion = m_installedPackage->GetLatestVersion(); if (installedVersion) { auto installedType = Manifest::ConvertToInstallerTypeEnum(installedVersion->GetMetadata()[PackageVersionMetadata::InstalledType]); @@ -560,7 +538,6 @@ namespace AppInstaller::Repository } std::shared_ptr m_installedPackage; - Utility::LocIndString m_installedChannel; Source m_trackingSource; std::shared_ptr m_trackingPackage; std::shared_ptr m_trackingPackageVersion; @@ -697,10 +674,11 @@ namespace AppInstaller::Repository // downloadManifests: when creating system reference strings, also download manifests to get more data. std::optional CheckForExistingResultFromAvailablePackageMatch(const ResultMatch& availableMatch, bool downloadManifests) { + std::shared_ptr availablePackage = OnlyAvailable(availableMatch.Package); + for (auto& match : Matches) { - const CompositePackage* compositeMatch = PackageCast(match.Package.get()); - if (compositeMatch && compositeMatch->IsSameAsAnyAvailable(availableMatch.Package.get())) + if (match.Package->IsSameAsAnyAvailable(availablePackage.get())) { if (ResultMatchComparator{}(availableMatch, match)) { @@ -714,9 +692,9 @@ namespace AppInstaller::Repository PackageData result; constexpr int c_downloadManifestsLimit = 3; int manifestsDownloaded = 0; - for (auto const& versionKey : availableMatch.Package->GetAvailableVersionKeys()) + for (auto const& versionKey : availablePackage->GetVersionKeys()) { - auto packageVersion = availableMatch.Package->GetAvailableVersion(versionKey); + auto packageVersion = availablePackage->GetVersion(versionKey); AddSystemReferenceStrings(packageVersion.get(), result); if (downloadManifests && manifestsDownloaded < c_downloadManifestsLimit) @@ -734,10 +712,12 @@ namespace AppInstaller::Repository // If we don't, return package data for further use. std::optional CheckForExistingResultFromTrackingPackageMatch(const ResultMatch& trackingMatch) { + std::shared_ptr trackingMatchPackage = OnlyAvailable(trackingMatch.Package); + for (auto& match : Matches) { const std::shared_ptr& trackingPackage = match.Package->GetTrackingPackage(); - if (trackingPackage && trackingPackage->IsSame(trackingMatch.Package.get())) + if (trackingPackage && trackingPackage->IsSame(trackingMatchPackage.get())) { if (ResultMatchComparator{}(trackingMatch, match)) { @@ -749,9 +729,9 @@ namespace AppInstaller::Repository } PackageData result; - for (auto const& versionKey : trackingMatch.Package->GetAvailableVersionKeys()) + for (auto const& versionKey : trackingMatchPackage->GetVersionKeys()) { - auto packageVersion = trackingMatch.Package->GetAvailableVersion(versionKey); + auto packageVersion = trackingMatchPackage->GetVersion(versionKey); AddSystemReferenceStrings(packageVersion.get(), result); } return result; @@ -923,7 +903,7 @@ namespace AppInstaller::Repository } }; - std::shared_ptr GetTrackedPackageFromAvailableSource(CompositeResult& result, const Source& source, const Utility::LocIndString& identifier) + std::shared_ptr GetTrackedPackageFromAvailableSource(CompositeResult& result, const Source& source, const Utility::LocIndString& identifier) { SearchRequest directRequest; directRequest.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, identifier.get()); @@ -936,7 +916,7 @@ namespace AppInstaller::Repository } else if (directResult.Matches.size() == 1) { - return std::move(directResult.Matches[0].Package); + return directResult.Matches[0].Package; } else { @@ -1049,9 +1029,14 @@ namespace AppInstaller::Repository continue; } - auto compositePackage = std::make_shared(match.Package); + std::shared_ptr compositePackage = std::make_shared(match.Package); + std::shared_ptr installedVersion; - auto installedVersion = compositePackage->GetInstalledVersion(); + auto installedPackage = compositePackage->GetInstalled(); + if (installedPackage) + { + installedVersion = installedPackage->GetLatestVersion(); + } if (!installedVersion) { @@ -1075,6 +1060,7 @@ namespace AppInstaller::Repository std::shared_ptr trackingPackage; std::shared_ptr trackingPackageVersion; std::chrono::system_clock::time_point trackingPackageTime; + bool foundAvailablePackageFromTracking = false; // Check the tracking catalog first to see if there is a correlation there. // TODO: When the issue with support for multiple available packages is fixed, this should move into @@ -1084,7 +1070,7 @@ namespace AppInstaller::Repository auto trackingCatalog = source.GetTrackingCatalog(); SearchResult trackingResult = trackingCatalog.Search(systemReferenceSearch); - std::shared_ptr candidatePackage = GetMatchingPackage(trackingResult.Matches, + std::shared_ptr candidatePackage = GetMatchingPackage(trackingResult.Matches, [&]() { AICLI_LOG(Repo, Info, << "Found multiple matches for installed package [" << installedVersion->GetProperty(PackageVersionProperty::Id) << @@ -1101,7 +1087,7 @@ namespace AppInstaller::Repository if (!trackingPackage || candidateTime > trackingPackageTime) { trackedSource = source; - trackingPackage = std::move(candidatePackage); + trackingPackage = OnlyAvailable(candidatePackage); trackingPackageVersion = std::move(candidateVersion); trackingPackageTime = candidateTime; } @@ -1115,6 +1101,7 @@ namespace AppInstaller::Repository if (availablePackage) { compositePackage->AddAvailablePackage(std::move(availablePackage)); + foundAvailablePackageFromTracking = true; } compositePackage->SetTracking(std::move(trackedSource), std::move(trackingPackage), std::move(trackingPackageVersion)); } @@ -1128,6 +1115,12 @@ namespace AppInstaller::Repository continue; } + // Skip the source that the tracking correlation result came from if we found one + if (foundAvailablePackageFromTracking && compositePackage->GetTrackingSource() == source) + { + continue; + } + SearchResult availableResult = result.SearchAndHandleFailures(source, systemReferenceSearch); if (availableResult.Matches.empty()) @@ -1146,7 +1139,7 @@ namespace AppInstaller::Repository }); // For non pinning cases. We found some matching packages here, don't keep going. - compositePackage->AddAvailablePackage(std::move(availablePackage)); + compositePackage->AddAvailablePackage(availablePackage); } } @@ -1189,7 +1182,7 @@ namespace AppInstaller::Repository // Correlate against installed (allow exceptions out as we own the installed source) SearchResult installedCrossRef = m_installedSource.Search(systemReferenceSearch); - std::shared_ptr installedPackage = GetMatchingPackage(installedCrossRef.Matches, + auto installedPackage = GetMatchingPackage(installedCrossRef.Matches, [&]() { AICLI_LOG(Repo, Info, << "Found multiple matches for tracking package [" << match.Package->GetProperty(PackageProperty::Id) << @@ -1198,15 +1191,15 @@ namespace AppInstaller::Repository AICLI_LOG(Repo, Warning, << " Appropriate installed package could not be determined"); }); - if (installedPackage && !result.ContainsInstalledPackage(installedPackage.get())) + if (installedPackage && !result.ContainsInstalledPackage(installedPackage->GetInstalled().get())) { auto compositePackage = std::make_shared( - std::move(installedPackage), + installedPackage, GetTrackedPackageFromAvailableSource(result, source, match.Package->GetProperty(PackageProperty::Id))); auto [writeTime, trackingPackageVersion] = GetLatestTrackingWriteTimeAndPackageVersion(match.Package); - compositePackage->SetTracking(source, std::move(match.Package), std::move(trackingPackageVersion)); + compositePackage->SetTracking(source, OnlyAvailable(match.Package), std::move(trackingPackageVersion)); result.Matches.emplace_back(std::move(compositePackage), match.MatchCriteria); } @@ -1241,7 +1234,7 @@ namespace AppInstaller::Repository // Correlate against installed (allow exceptions out as we own the installed source) SearchResult installedCrossRef = m_installedSource.Search(systemReferenceSearch); - std::shared_ptr installedPackage = GetMatchingPackage(installedCrossRef.Matches, + auto installedPackage = GetMatchingPackage(installedCrossRef.Matches, [&]() { AICLI_LOG(Repo, Info, << "Found multiple matches for available package [" << match.Package->GetProperty(PackageProperty::Id) << @@ -1250,18 +1243,18 @@ namespace AppInstaller::Repository AICLI_LOG(Repo, Warning, << " Appropriate installed package could not be determined"); }); - if (installedPackage && !result.ContainsInstalledPackage(installedPackage.get())) + if (installedPackage && !result.ContainsInstalledPackage(installedPackage->GetInstalled().get())) { // TODO: Needs a whole separate change to fix the fact that we don't support multiple available packages and what the different search behaviors mean foundInstalledMatch = true; - result.Matches.emplace_back(std::make_shared(std::move(installedPackage), std::move(match.Package)), match.MatchCriteria); + result.Matches.emplace_back(std::make_shared(installedPackage, std::move(match.Package)), match.MatchCriteria); } } // If there was no correlation for this package, add it without one. if ((m_searchBehavior == CompositeSearchBehavior::AllPackages || m_searchBehavior == CompositeSearchBehavior::AvailablePackages) && !foundInstalledMatch) { - result.Matches.emplace_back(std::make_shared(std::shared_ptr{}, std::move(match.Package)), match.MatchCriteria); + result.Matches.emplace_back(std::make_shared(std::shared_ptr{}, std::move(match.Package)), match.MatchCriteria); } } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index 908d0fc4fc..34f7d8bd7e 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -214,17 +214,20 @@ namespace AppInstaller::Repository::Microsoft SQLiteIndex::IdType m_manifestId; }; - // The base for IPackage implementations here. - struct PackageBase : public SourceReference + // The IPackage implementation here. + struct SQLitePackage : public std::enable_shared_from_this, public SourceReference, public IPackage, public ICompositePackage { - PackageBase(const std::shared_ptr& source, SQLiteIndex::IdType idId) : - SourceReference(source), m_idId(idId) {} + static constexpr IPackageType PackageType = IPackageType::SQLitePackage; + SQLitePackage(const std::shared_ptr& source, SQLiteIndex::IdType idId, bool isInstalled) : + SourceReference(source), m_idId(idId), m_isInstalled(isInstalled) {} + + // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const { Utility::LocIndString result; - std::shared_ptr truth = GetLatestVersionInternal(); + std::shared_ptr truth = GetLatestVersion(); if (truth) { switch (property) @@ -239,70 +242,30 @@ namespace AppInstaller::Repository::Microsoft } else { - AICLI_LOG(Repo, Verbose, << "PackageBase: No manifest was found for the package with id# '" << m_idId << "'"); + AICLI_LOG(Repo, Verbose, << "SQLitePackage: No manifest was found for the package with id# '" << m_idId << "'"); } return result; } - bool IsSame(const PackageBase& other) const - { - return GetReferenceSource()->IsSame(other.GetReferenceSource().get()) && m_idId == other.m_idId; - } - - protected: - std::shared_ptr GetLatestVersionInternal() const + std::vector GetVersionKeys() const override { std::shared_ptr source = GetReferenceSource(); - std::optional manifestId = source->GetIndex().GetManifestIdByKey(m_idId, {}, {}); - if (manifestId) { - return std::make_shared(source, manifestId.value()); - } - - return {}; - } + auto sharedLock = m_versionKeysLock.lock_shared(); - SQLiteIndex::IdType m_idId; - }; - - // The IPackage impl for SQLiteIndexSource of Available packages. - struct AvailablePackage : public PackageBase, public IPackage - { - using PackageBase::PackageBase; - - static constexpr IPackageType PackageType = IPackageType::SQLiteAvailablePackage; - - // Inherited via IPackage - Utility::LocIndString GetProperty(PackageProperty property) const override - { - return PackageBase::GetProperty(property); - } - - std::shared_ptr GetInstalledVersion() const override - { - return {}; - } - - std::vector GetAvailableVersionKeys() const override - { - std::shared_ptr source = GetReferenceSource(); - - { - auto sharedLock = m_availableVersionKeysLock.lock_shared(); - - if (!m_availableVersionKeys.empty()) + if (!m_versionKeys.empty()) { - return m_availableVersionKeys; + return m_versionKeys; } } - auto exclusiveLock = m_availableVersionKeysLock.lock_exclusive(); + auto exclusiveLock = m_versionKeysLock.lock_exclusive(); - if (!m_availableVersionKeys.empty()) + if (!m_versionKeys.empty()) { - return m_availableVersionKeys; + return m_versionKeys; } std::vector versions = source->GetIndex().GetVersionKeysById(m_idId); @@ -311,19 +274,27 @@ namespace AppInstaller::Repository::Microsoft { std::string version = vk.VersionAndChannel.GetVersion().ToString(); std::string channel = vk.VersionAndChannel.GetChannel().ToString(); - m_availableVersionKeys.emplace_back(source->GetIdentifier(), version, channel); - m_availableVersionKeysMap.emplace(MapKey{ std::move(version), std::move(channel) }, vk.ManifestId); + m_versionKeys.emplace_back(source->GetIdentifier(), version, channel); + m_versionKeysMap.emplace(MapKey{ std::move(version), std::move(channel) }, vk.ManifestId); } - return m_availableVersionKeys; + return m_versionKeys; } - std::shared_ptr GetLatestAvailableVersion() const override + std::shared_ptr GetLatestVersion() const override { - return GetLatestVersionInternal(); + std::shared_ptr source = GetReferenceSource(); + std::optional manifestId = source->GetIndex().GetManifestIdByKey(m_idId, {}, {}); + + if (manifestId) + { + return std::make_shared(source, manifestId.value()); + } + + return {}; } - std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const override + std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override { std::shared_ptr source = GetReferenceSource(); @@ -337,10 +308,10 @@ namespace AppInstaller::Repository::Microsoft { MapKey requested{ versionKey.Version, versionKey.Channel }; - auto sharedLock = m_availableVersionKeysLock.lock_shared(); + auto sharedLock = m_versionKeysLock.lock_shared(); - auto itr = m_availableVersionKeysMap.find(requested); - if (itr != m_availableVersionKeysMap.end()) + auto itr = m_versionKeysMap.find(requested); + if (itr != m_versionKeysMap.end()) { manifestId = itr->second; } @@ -359,13 +330,18 @@ namespace AppInstaller::Repository::Microsoft return {}; } + Source GetSource() const override + { + return Source{ GetReferenceSource() }; + } + bool IsSame(const IPackage* other) const override { - const AvailablePackage* otherAvailable = PackageCast(other); + const SQLitePackage* otherSQLite = PackageCast(other); - if (otherAvailable) + if (otherSQLite) { - return PackageBase::IsSame(*otherAvailable); + return GetReferenceSource()->IsSame(otherSQLite->GetReferenceSource().get()) && m_idId == otherSQLite->m_idId; } return false; @@ -381,6 +357,17 @@ namespace AppInstaller::Repository::Microsoft return nullptr; } + // Inherited via ICompositePackage + std::shared_ptr GetInstalled() override + { + return m_isInstalled ? shared_from_this() : std::shared_ptr{}; + } + + std::vector> GetAvailable() override + { + return m_isInstalled ? std::vector>{} : std::vector>{ shared_from_this() }; + } + private: // Contains the information needed to map a version key to it's rows. struct MapKey @@ -405,66 +392,13 @@ namespace AppInstaller::Repository::Microsoft } }; - // To avoid removing const from the interface - mutable wil::srwlock m_availableVersionKeysLock; - mutable std::vector m_availableVersionKeys; - mutable std::map m_availableVersionKeysMap; - }; - - // The IPackage impl for SQLiteIndexSource of Installed packages. - struct InstalledPackage : public PackageBase, public IPackage - { - using PackageBase::PackageBase; - - static constexpr IPackageType PackageType = IPackageType::SQLiteInstalledPackage; - - // Inherited via IPackage - Utility::LocIndString GetProperty(PackageProperty property) const override - { - return PackageBase::GetProperty(property); - } - - std::shared_ptr GetInstalledVersion() const override - { - return GetLatestVersionInternal(); - } - - std::vector GetAvailableVersionKeys() const override - { - return {}; - } - - std::shared_ptr GetLatestAvailableVersion() const override - { - return {}; - } - - std::shared_ptr GetAvailableVersion(const PackageVersionKey&) const override - { - return {}; - } - - bool IsSame(const IPackage* other) const override - { - const InstalledPackage* otherInstalled = PackageCast(other); - - if (otherInstalled) - { - return PackageBase::IsSame(*otherInstalled); - } - - return false; - } - - const void* CastTo(IPackageType type) const override - { - if (type == PackageType) - { - return this; - } + SQLiteIndex::IdType m_idId; + bool m_isInstalled; - return nullptr; - } + // To avoid removing const from the interface + mutable wil::srwlock m_versionKeysLock; + mutable std::vector m_versionKeys; + mutable std::map m_versionKeysMap; }; } @@ -495,18 +429,9 @@ namespace AppInstaller::Repository::Microsoft std::shared_ptr sharedThis = NonConstSharedFromThis(); for (auto& indexResult : indexResults.Matches) { - std::unique_ptr package; - - if (m_isInstalled) - { - package = std::make_unique(sharedThis, indexResult.first); - } - else - { - package = std::make_unique(sharedThis, indexResult.first); - } - - result.Matches.emplace_back(std::move(package), std::move(indexResult.second)); + result.Matches.emplace_back( + std::make_shared(sharedThis, indexResult.first, m_isInstalled), + std::move(indexResult.second)); } result.Truncated = indexResults.Truncated; return result; diff --git a/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp b/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp index a55c69ee27..c69090d7e1 100644 --- a/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp +++ b/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include "Public/winget/RepositorySearch.h" +#include "Public/winget/InstalledStatus.h" +#include "Public/winget/PackageVersionSelection.h" #include using namespace AppInstaller::Settings; @@ -77,14 +78,14 @@ namespace AppInstaller::Repository } std::vector CheckInstalledStatusInternal( - const std::shared_ptr& package, + const std::shared_ptr& package, InstalledStatusType checkTypes) { using namespace AppInstaller::Manifest; std::vector result; bool checkFileHash = false; - std::shared_ptr installedVersion = package->GetInstalledVersion(); + std::shared_ptr installedVersion = GetInstalledVersion(package); std::shared_ptr availableVersion; FileHashMap fileHashes; @@ -96,6 +97,8 @@ namespace AppInstaller::Repository Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; HRESULT installedLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + std::shared_ptr availableVersions = GetAvailableVersionsForInstalledVersion(package); + // Prepare installed metadata from installed version. // Determine the available package version to be used for installed status checking. // Only perform file hash check if we find an available version that matches installed version. @@ -120,14 +123,14 @@ namespace AppInstaller::Repository { // Use the base version as available version if installed version is mapped to be an approximate. versionKey.Version = installedVersionAsVersion.GetBaseVersion().ToString(); - availableVersion = package->GetAvailableVersion(versionKey); + availableVersion = availableVersions->GetVersion(versionKey); // It's unexpected if the installed version is already mapped to some version. THROW_HR_IF(E_UNEXPECTED, !availableVersion); } else { versionKey.Version = installedVersionAsVersion.ToString(); - availableVersion = package->GetAvailableVersion(versionKey); + availableVersion = availableVersions->GetVersion(versionKey); if (availableVersion) { checkFileHash = true; @@ -139,7 +142,7 @@ namespace AppInstaller::Repository { // No installed version, or installed version not found in available versions, // then attempt to check installed status using latest version. - availableVersion = package->GetLatestAvailableVersion(); + availableVersion = availableVersions->GetLatestVersion(); THROW_HR_IF(E_UNEXPECTED, !availableVersion); } @@ -237,7 +240,7 @@ namespace AppInstaller::Repository } } - std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes) + std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes) { return CheckInstalledStatusInternal(package, checkTypes); } diff --git a/src/AppInstallerRepositoryCore/PackageVersionSelection.cpp b/src/AppInstallerRepositoryCore/PackageVersionSelection.cpp new file mode 100644 index 0000000000..a5e9720ac4 --- /dev/null +++ b/src/AppInstallerRepositoryCore/PackageVersionSelection.cpp @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/PackageVersionSelection.h" +#include "Public/winget/RepositorySource.h" + + +namespace AppInstaller::Repository +{ + namespace + { + std::shared_ptr GetAvailablePackageFromSource(const std::vector>& packages, const std::string_view sourceIdentifier) + { + for (const std::shared_ptr& package : packages) + { + if (sourceIdentifier == package->GetSource().GetIdentifier()) + { + return package; + } + } + + return {}; + } + + struct AvailablePackageVersionCollection : public IPackageVersionCollection + { + AvailablePackageVersionCollection(const std::shared_ptr& composite, const std::shared_ptr& installedVersion) : + m_packages(composite->GetAvailable()) + { + if (!installedVersion) + { + return; + } + + m_channel = installedVersion->GetProperty(PackageVersionProperty::Channel); + + // Remove the packages that are not from the installed source. + Source installedVersionSource = installedVersion->GetSource(); + if (installedVersionSource && installedVersionSource.ContainsAvailablePackages()) + { + m_packages.erase(std::remove_if(m_packages.begin(), m_packages.end(), [&](const std::shared_ptr& p) { return installedVersionSource != p->GetSource(); }), m_packages.end()); + } + } + + std::vector GetVersionKeys() const override + { + std::vector result; + + for (const std::shared_ptr& package : m_packages) + { + std::vector versionKeys = package->GetVersionKeys(); + std::copy(versionKeys.begin(), versionKeys.end(), std::back_inserter(result)); + } + + // Remove all elements whose channel does not match the installed package. + if (m_channel) + { + result.erase( + std::remove_if(result.begin(), result.end(), [&](const PackageVersionKey& pvk) { return !Utility::ICUCaseInsensitiveEquals(pvk.Channel, m_channel.value()); }), + result.end()); + } + + // Put latest versions at the front; for versions available from multiple sources maintain the order they were added in + std::stable_sort(result.begin(), result.end()); + + return result; + } + + std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override + { + // If there is a specific source, just use that package's result + std::shared_ptr package; + + if (!versionKey.SourceId.empty()) + { + package = GetAvailablePackageFromSource(m_packages, versionKey.SourceId); + } + else + { + // Otherwise, find the first version that matches + std::vector versions = GetVersionKeys(); + + for (const PackageVersionKey& key : versions) + { + if (key.IsMatch(versionKey)) + { + package = GetAvailablePackageFromSource(m_packages, key.SourceId); + break; + } + } + } + + return package ? package->GetVersion(versionKey) : nullptr; + } + + std::shared_ptr GetLatestVersion() const override + { + return GetVersion({ "", "", m_channel.value_or("") }); + } + + private: + std::optional m_channel; + std::vector> m_packages; + }; + } + + std::shared_ptr GetAvailableVersionsForInstalledVersion(const std::shared_ptr& composite) + { + return std::make_shared(composite, GetInstalledVersion(composite)); + } + + std::shared_ptr GetAvailableVersionsForInstalledVersion( + const std::shared_ptr& composite, + const std::shared_ptr& installedVersion) + { + return std::make_shared(composite, installedVersion); + } + + std::shared_ptr GetAllAvailableVersions(const std::shared_ptr& composite) + { + return GetAvailableVersionsForInstalledVersion(composite, nullptr); + } + + std::shared_ptr GetInstalledVersion(const std::shared_ptr& composite) + { + auto installedPackage = composite->GetInstalled(); + return installedPackage ? installedPackage->GetLatestVersion() : nullptr; + } + + std::shared_ptr GetAvailablePackageFromSource(const std::shared_ptr& composite, const std::string_view sourceIdentifier) + { + return GetAvailablePackageFromSource(composite->GetAvailable(), sourceIdentifier); + } +} diff --git a/src/AppInstallerRepositoryCore/PinningData.cpp b/src/AppInstallerRepositoryCore/PinningData.cpp index e0ee78ddc6..ac29bb0d0a 100644 --- a/src/AppInstallerRepositoryCore/PinningData.cpp +++ b/src/AppInstallerRepositoryCore/PinningData.cpp @@ -145,19 +145,19 @@ namespace AppInstaller::Pinning PinningData::PinStateEvaluator::~PinStateEvaluator() = default; - std::shared_ptr PinningData::PinStateEvaluator::GetLatestAvailableVersionForPins(const std::shared_ptr& package) + std::shared_ptr PinningData::PinStateEvaluator::GetLatestAvailableVersionForPins(const std::shared_ptr& package) { if (!m_database) { - return package->GetLatestAvailableVersion(); + return package->GetLatestVersion(); } - auto availableVersionKeys = package->GetAvailableVersionKeys(); + auto availableVersionKeys = package->GetVersionKeys(); // Skip until we find a version that isn't pinned for (const auto& availableVersion : availableVersionKeys) { - std::shared_ptr packageVersion = package->GetAvailableVersion(availableVersion); + std::shared_ptr packageVersion = package->GetVersion(availableVersion); if (EvaluatePinType(packageVersion) == Pinning::PinType::Unknown) { return packageVersion; diff --git a/src/AppInstallerRepositoryCore/Public/winget/InstalledStatus.h b/src/AppInstallerRepositoryCore/Public/winget/InstalledStatus.h new file mode 100644 index 0000000000..fc2d803338 --- /dev/null +++ b/src/AppInstallerRepositoryCore/Public/winget/InstalledStatus.h @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include +#include +#include + +#include +#include + + +namespace AppInstaller::Repository +{ + // Defines the installed status check type. + enum class InstalledStatusType : uint32_t + { + // None is checked. + None = 0x0, + // Check Apps and Features entry. + AppsAndFeaturesEntry = 0x0001, + // Check Apps and Features entry install location if applicable. + AppsAndFeaturesEntryInstallLocation = 0x0002, + // Check Apps and Features entry install location with installed files if applicable. + AppsAndFeaturesEntryInstallLocationFile = 0x0004, + // Check default install location if applicable. + DefaultInstallLocation = 0x0008, + // Check default install location with installed files if applicable. + DefaultInstallLocationFile = 0x0010, + + // Below are helper values for calling CheckInstalledStatus as input. + // AppsAndFeaturesEntry related checks + AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, + // DefaultInstallLocation related checks + AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, + // All checks + AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, + }; + + DEFINE_ENUM_FLAG_OPERATORS(InstalledStatusType); + + // Struct representing an individual installed status. + struct InstalledStatus + { + // The installed status type. + InstalledStatusType Type = InstalledStatusType::None; + // The installed status path. + Utility::NormalizedString Path; + // The installed status result. + HRESULT Status; + + InstalledStatus(InstalledStatusType type, Utility::NormalizedString path, HRESULT status) : + Type(type), Path(std::move(path)), Status(status) {} + }; + + // Struct representing installed status from an installer. + struct InstallerInstalledStatus + { + Manifest::ManifestInstaller Installer; + std::vector Status; + }; + + // Checks installed status of a package. + std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes = InstalledStatusType::AllChecks); +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/PackageVersionSelection.h b/src/AppInstallerRepositoryCore/Public/winget/PackageVersionSelection.h new file mode 100644 index 0000000000..e9a5b5edbd --- /dev/null +++ b/src/AppInstallerRepositoryCore/Public/winget/PackageVersionSelection.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include + + +namespace AppInstaller::Repository +{ + // Gets an IPackageVersionCollection that represents the available package versions for the installed version. + // If we have tracking data, will remove packages not from the tracked source. Will also remove versions that do not correspond to the tracked channel. + // This function uses the latest installed version as a temporary convenience until side-by-side is implemented. + std::shared_ptr GetAvailableVersionsForInstalledVersion(const std::shared_ptr& composite); + + // Gets an IPackageVersionCollection that represents the available package versions for the given installed version. + std::shared_ptr GetAvailableVersionsForInstalledVersion( + const std::shared_ptr& composite, + const std::shared_ptr& installedVersion); + + // Equivalent to `GetAvailableVersionsForInstalledVersion(composite, nullptr)` to make the intent more clear that the caller wants to ignore any installed + // package information. + std::shared_ptr GetAllAvailableVersions(const std::shared_ptr& composite); + + // Gets the installed version, or a null if there isn't one. + std::shared_ptr GetInstalledVersion(const std::shared_ptr& composite); + + // Gets the available IPackage corresponding to the given source identifier. + std::shared_ptr GetAvailablePackageFromSource(const std::shared_ptr& composite, const std::string_view sourceIdentifier); +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/PinningData.h b/src/AppInstallerRepositoryCore/Public/winget/PinningData.h index 94f56499d0..ca1e3b36ce 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/PinningData.h +++ b/src/AppInstallerRepositoryCore/Public/winget/PinningData.h @@ -79,7 +79,7 @@ namespace AppInstaller::Pinning // Gets the latest available package version that fits within the pinning restrictions. // This should be the package object that contains available versions associated with the installed version for which this evaluator was created. - std::shared_ptr GetLatestAvailableVersionForPins(const std::shared_ptr& package); + std::shared_ptr GetLatestAvailableVersionForPins(const std::shared_ptr& package); // Determines if the given version is an update to the installed version that this object was created with. // This should be a version associated with the installed version for which this evaluator was created. diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 528551b58c..1ad88150ad 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -260,8 +260,11 @@ namespace AppInstaller::Repository // The order for the sources depends on the context. return Utility::VersionAndChannel({ Version }, { Channel }) < Utility::VersionAndChannel({ other.Version }, { other.Channel }); } - }; + // Determines if a well defined key (this one) is matched by the provided key. + // The provided key may use empty values to indicate no specific matching requirements. + bool IsMatch(const PackageVersionKey& other) const; + }; // A property of a package. enum class PackageProperty @@ -270,87 +273,43 @@ namespace AppInstaller::Repository Name, }; - // Defines the installed status check type. - enum class InstalledStatusType : uint32_t - { - // None is checked. - None = 0x0, - // Check Apps and Features entry. - AppsAndFeaturesEntry = 0x0001, - // Check Apps and Features entry install location if applicable. - AppsAndFeaturesEntryInstallLocation = 0x0002, - // Check Apps and Features entry install location with installed files if applicable. - AppsAndFeaturesEntryInstallLocationFile = 0x0004, - // Check default install location if applicable. - DefaultInstallLocation = 0x0008, - // Check default install location with installed files if applicable. - DefaultInstallLocationFile = 0x0010, - - // Below are helper values for calling CheckInstalledStatus as input. - // AppsAndFeaturesEntry related checks - AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, - // DefaultInstallLocation related checks - AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, - // All checks - AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, - }; - - DEFINE_ENUM_FLAG_OPERATORS(InstalledStatusType); - - // Struct representing an individual installed status. - struct InstalledStatus - { - // The installed status type. - InstalledStatusType Type = InstalledStatusType::None; - // The installed status path. - Utility::NormalizedString Path; - // The installed status result. - HRESULT Status; - - InstalledStatus(InstalledStatusType type, Utility::NormalizedString path, HRESULT status) : - Type(type), Path(std::move(path)), Status(status) {} - }; - - // Struct representing installed status from an installer. - struct InstallerInstalledStatus - { - Manifest::ManifestInstaller Installer; - std::vector Status; - }; - // To allow for runtime casting from IPackage to the specific types, this enum contains all of the IPackage implementations. enum class IPackageType { TestPackage, - RestAvailablePackage, - SQLiteAvailablePackage, - SQLiteInstalledPackage, + RestPackage, + SQLitePackage, PinnablePackage, - CompositePackage, + CompositeInstalledPackage, }; - // A package, potentially containing information about it's local state and the available versions. - struct IPackage + // Contains a collection of package versions. + struct IPackageVersionCollection { - virtual ~IPackage() = default; + virtual ~IPackageVersionCollection() = default; - // Gets a property of this package. - virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; - - // Gets the installed package information. - virtual std::shared_ptr GetInstalledVersion() const = 0; - - // Gets all available versions of this package. + // Gets all versions of this package. // The versions will be returned in sorted, descending order. // Ex. { 4, 3, 2, 1 } - // The list may contain versions from multiple sources. - virtual std::vector GetAvailableVersionKeys() const = 0; + virtual std::vector GetVersionKeys() const = 0; // Gets a specific version of this package. - virtual std::shared_ptr GetLatestAvailableVersion() const = 0; + virtual std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const = 0; - // Gets a specific version of this package. - virtual std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const = 0; + // A convenience method to effectively call `GetVersion(GetVersionKeys[0])`. + virtual std::shared_ptr GetLatestVersion() const = 0; + }; + + // Contains information about a package and its versions from a single source. + struct IPackage : public IPackageVersionCollection + { + virtual ~IPackage() = default; + + // Gets a property of this package. + virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; + + // Gets the source that this package is from. + virtual Source GetSource() const = 0; // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; @@ -359,6 +318,22 @@ namespace AppInstaller::Repository virtual const void* CastTo(IPackageType type) const = 0; }; + // Contains information about the graph of packages related to a search. + struct ICompositePackage + { + virtual ~ICompositePackage() = default; + + // Gets a property of this package result. + virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; + + // Gets the installed package information. + virtual std::shared_ptr GetInstalled() = 0; + + // Gets all of the available packages for this result. + // There will be at most one package per source in this list. + virtual std::vector> GetAvailable() = 0; + }; + // Does the equivalent of a dynamic_cast, but without it to allow RTTI to be disabled. // Example usage: // bool IsSame(const IPackage* other) const override @@ -382,12 +357,12 @@ namespace AppInstaller::Repository struct ResultMatch { // The package found by the search request. - std::shared_ptr Package; + std::shared_ptr Package; // The highest order field on which the package matched the search. PackageMatchFilter MatchCriteria; - ResultMatch(std::shared_ptr p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} + ResultMatch(std::shared_ptr p, PackageMatchFilter f) : Package(std::move(p)), MatchCriteria(std::move(f)) {} }; // Search result data. @@ -433,7 +408,4 @@ namespace AppInstaller::Repository private: mutable std::string m_whatMessage; }; - - // Checks installed status of a package. - std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes = InstalledStatusType::AllChecks); } diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h index 486e69d524..33a8acc5bf 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h @@ -220,6 +220,11 @@ namespace AppInstaller::Repository // To avoid putting try catch everywhere, we use bool operator here. operator bool() const; + // Determines if the sources are equivalent. + // Currently only works for individual sources, not composites. + bool operator==(const Source& other) const; + bool operator!=(const Source& other) const; + // Gets the source's identifier; a unique identifier independent of the name // that will not change between a remove/add or between additional adds. // Must be suitable for filesystem names unless the source is internal to winget, diff --git a/src/AppInstallerRepositoryCore/RepositorySearch.cpp b/src/AppInstallerRepositoryCore/RepositorySearch.cpp index 2384b3b893..749187aaed 100644 --- a/src/AppInstallerRepositoryCore/RepositorySearch.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySearch.cpp @@ -92,6 +92,14 @@ namespace AppInstaller::Repository } } + bool PackageVersionKey::IsMatch(const PackageVersionKey& other) const + { + return + ((other.SourceId.empty() || other.SourceId == SourceId) && + (other.Version.empty() || Utility::ICUCaseInsensitiveEquals(other.Version, Version)) && + (other.Channel.empty() || Utility::ICUCaseInsensitiveEquals(other.Channel, Channel))); + } + const char* UnsupportedRequestException::what() const noexcept { if (m_whatMessage.empty()) diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp index bee2f4a4dc..f3899b104c 100644 --- a/src/AppInstallerRepositoryCore/RepositorySource.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -473,6 +473,19 @@ namespace AppInstaller::Repository } } + bool Source::operator==(const Source& other) const + { + SourceDetails thisDetails = GetDetails(); + SourceDetails otherDetails = other.GetDetails(); + + return (thisDetails.Type == otherDetails.Type && thisDetails.Identifier == otherDetails.Identifier); + } + + bool Source::operator!=(const Source& other) const + { + return !operator==(other); + } + std::string Source::GetIdentifier() const { if (m_source) diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index 22e0e05de5..0bba3c84dc 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -30,11 +30,11 @@ namespace AppInstaller::Repository::Rest }; // The IPackage implementation for Available packages from RestSource. - struct AvailablePackage : public std::enable_shared_from_this, public SourceReference, public IPackage + struct RestPackage : public std::enable_shared_from_this, public SourceReference, public IPackage, public ICompositePackage { - static constexpr IPackageType PackageType = IPackageType::RestAvailablePackage; + static constexpr IPackageType PackageType = IPackageType::RestPackage; - AvailablePackage(const std::shared_ptr& source, IRestClient::Package&& package) : + RestPackage(const std::shared_ptr& source, IRestClient::Package&& package) : SourceReference(source), m_package(std::move(package)) { SortVersionsInternal(); @@ -54,12 +54,7 @@ namespace AppInstaller::Repository::Rest } } - std::shared_ptr GetInstalledVersion() const override - { - return {}; - } - - std::vector GetAvailableVersionKeys() const override + std::vector GetVersionKeys() const override { std::shared_ptr source = GetReferenceSource(); std::scoped_lock versionsLock{ m_packageVersionsLock }; @@ -74,22 +69,27 @@ namespace AppInstaller::Repository::Rest return result; } - std::shared_ptr GetLatestAvailableVersion() const override + std::shared_ptr GetLatestVersion() const override { std::scoped_lock versionsLock{ m_packageVersionsLock }; return GetLatestVersionInternal(); } - std::shared_ptr GetAvailableVersion(const PackageVersionKey& versionKey) const override; + std::shared_ptr GetVersion(const PackageVersionKey& versionKey) const override; + + Source GetSource() const override + { + return Source{ GetReferenceSource() }; + } bool IsSame(const IPackage* other) const override { - const AvailablePackage* otherAvailablePackage = PackageCast(other); + const RestPackage* otherPackage = PackageCast(other); - if (otherAvailablePackage) + if (otherPackage) { - return GetReferenceSource()->IsSame(otherAvailablePackage->GetReferenceSource().get()) && - Utility::CaseInsensitiveEquals(m_package.PackageInformation.PackageIdentifier, otherAvailablePackage->m_package.PackageInformation.PackageIdentifier); + return GetReferenceSource()->IsSame(otherPackage->GetReferenceSource().get()) && + Utility::CaseInsensitiveEquals(m_package.PackageInformation.PackageIdentifier, otherPackage->m_package.PackageInformation.PackageIdentifier); } return false; @@ -105,6 +105,17 @@ namespace AppInstaller::Repository::Rest return nullptr; } + // Inherited via ICompositePackage + std::shared_ptr GetInstalled() override + { + return {}; + } + + std::vector> GetAvailable() override + { + return std::vector>{ shared_from_this() }; + } + // Helpers for PackageVersion interop const IRestClient::PackageInfo& PackageInfo() const { @@ -161,9 +172,9 @@ namespace AppInstaller::Repository::Rest } private: - std::shared_ptr NonConstSharedFromThis() const + std::shared_ptr NonConstSharedFromThis() const { - return const_cast(this)->shared_from_this(); + return const_cast(this)->shared_from_this(); } // Must hold m_packageVersionsLock while calling this @@ -188,7 +199,7 @@ namespace AppInstaller::Repository::Rest struct PackageVersion : public SourceReference, public IPackageVersion { PackageVersion( - const std::shared_ptr& source, std::shared_ptr&& package, IRestClient::VersionInfo versionInfo) + const std::shared_ptr& source, std::shared_ptr&& package, IRestClient::VersionInfo versionInfo) : SourceReference(source), m_package(std::move(package)), m_versionInfo(std::move(versionInfo)) {} // Inherited via IPackageVersion @@ -347,11 +358,11 @@ namespace AppInstaller::Repository::Rest } private: - std::shared_ptr m_package; + std::shared_ptr m_package; IRestClient::VersionInfo m_versionInfo; }; - std::shared_ptr AvailablePackage::GetAvailableVersion(const PackageVersionKey& versionKey) const + std::shared_ptr RestPackage::GetVersion(const PackageVersionKey& versionKey) const { std::shared_ptr source = GetReferenceSource(); std::scoped_lock versionsLock{ m_packageVersionsLock }; @@ -405,7 +416,7 @@ namespace AppInstaller::Repository::Rest return packageVersion; } - std::shared_ptr AvailablePackage::GetLatestVersionInternal() const + std::shared_ptr RestPackage::GetLatestVersionInternal() const { return std::make_shared(GetReferenceSource(), NonConstSharedFromThis(), m_package.Versions.front()); } @@ -450,7 +461,7 @@ namespace AppInstaller::Repository::Rest std::shared_ptr sharedThis = NonConstSharedFromThis(); for (auto& result : results.Matches) { - std::shared_ptr package = std::make_shared(sharedThis, std::move(result)); + std::shared_ptr package = std::make_shared(sharedThis, std::move(result)); // TODO: Improve to use Package match filter to return relevant search results. PackageMatchFilter packageFilter{ {}, {}, {} }; diff --git a/src/AppInstallerSharedLib/Yaml.cpp b/src/AppInstallerSharedLib/Yaml.cpp index 101829a2e1..aa856789c2 100644 --- a/src/AppInstallerSharedLib/Yaml.cpp +++ b/src/AppInstallerSharedLib/Yaml.cpp @@ -407,14 +407,17 @@ namespace AppInstaller::YAML std::optional Node::try_as_dispatch(int64_t*) const { - try - { - return std::optional{ std::stoll(m_scalar) }; - } - catch(...) + const char* begin = m_scalar.c_str(); + char* end = nullptr; + errno = 0; + int64_t result = static_cast(strtoll(begin, &end, 0)); + + if (errno == ERANGE || static_cast(end - begin) != m_scalar.length()) { return {}; } + + return result; } int Node::as_dispatch(int*) const diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp index 701f71eb97..756d6fbd60 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.cpp +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -1,127 +1,130 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include -#include -#include "CatalogPackage.h" -#include "CatalogPackage.g.cpp" -#include "PackageCatalog.h" -#include "PackageVersionInfo.h" -#include "PackageVersionId.h" -#include "PackageInstallerInstalledStatus.h" -#include "CheckInstalledStatusResult.h" -#include -#include - - -namespace winrt::Microsoft::Management::Deployment::implementation -{ - void CatalogPackage::Initialize( - ::AppInstaller::Repository::Source source, - std::shared_ptr<::AppInstaller::Repository::IPackage> package) - { - m_source = std::move(source); - m_package = std::move(package); - } - hstring CatalogPackage::Id() - { - return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Id).get()); - } - hstring CatalogPackage::Name() - { - return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Name)); - } - Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::InstalledVersion() - { - std::call_once(m_installedVersionOnceFlag, - [&]() - { - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> installedVersion = m_package.get()->GetInstalledVersion(); - if (installedVersion) - { - auto installedVersionImpl = winrt::make_self>(); - installedVersionImpl->Initialize(std::move(installedVersion)); - m_installedVersion = *installedVersionImpl; - } - }); - return m_installedVersion; - } - Windows::Foundation::Collections::IVectorView CatalogPackage::AvailableVersions() - { - std::call_once(m_availableVersionsOnceFlag, - [&]() - { - // Vector hasn't been populated yet. - for (auto const& versionKey : m_package.get()->GetAvailableVersionKeys()) - { - auto packageVersionId = winrt::make_self>(); - packageVersionId->Initialize(versionKey); - m_availableVersions.Append(*packageVersionId); - } - }); - return m_availableVersions.GetView(); - } - - void CatalogPackage::InitializeDefaultInstallVersion() - { - std::call_once(m_defaultInstallVersionOnceFlag, - [&]() - { - using namespace AppInstaller::Pinning; - - PinningData pinningData{ PinningData::Disposition::ReadOnly }; - auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, m_package->GetInstalledVersion()); - - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> latestVersion = evaluator.GetLatestAvailableVersionForPins(m_package); - if (latestVersion) - { - m_updateAvailable = evaluator.IsUpdate(latestVersion); - - // DefaultInstallVersion hasn't been created yet, create and populate it. - // DefaultInstallVersion is the LatestAvailableVersion of the internal package object. - auto latestVersionImpl = winrt::make_self>(); - latestVersionImpl->Initialize(std::move(latestVersion)); - m_defaultInstallVersion = *latestVersionImpl; - } - }); - } - - Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() - { - InitializeDefaultInstallVersion(); - return m_defaultInstallVersion; - } - - Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::GetPackageVersionInfo(Microsoft::Management::Deployment::PackageVersionId const& versionKey) - { - winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; - - ::AppInstaller::Repository::PackageVersionKey internalVersionKey(winrt::to_string(versionKey.PackageCatalogId()), winrt::to_string(versionKey.Version()), winrt::to_string(versionKey.Channel())); - std::shared_ptr<::AppInstaller::Repository::IPackageVersion> availableVersion = m_package.get()->GetAvailableVersion(internalVersionKey); - if (availableVersion) - { - auto packageVersionInfoImpl = winrt::make_self>(); - packageVersionInfoImpl->Initialize(std::move(availableVersion)); - packageVersionInfo =*packageVersionInfoImpl; - } - return packageVersionInfo; - } - - bool CatalogPackage::IsUpdateAvailable() - { - InitializeDefaultInstallVersion(); - return m_updateAvailable; - } - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include +#include +#include "CatalogPackage.h" +#include "CatalogPackage.g.cpp" +#include "PackageCatalog.h" +#include "PackageVersionInfo.h" +#include "PackageVersionId.h" +#include "PackageInstallerInstalledStatus.h" +#include "CheckInstalledStatusResult.h" +#include +#include +#include + + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void CatalogPackage::Initialize( + ::AppInstaller::Repository::Source source, + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> package) + { + m_source = std::move(source); + m_package = std::move(package); + } + hstring CatalogPackage::Id() + { + return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Id).get()); + } + hstring CatalogPackage::Name() + { + return winrt::to_hstring(m_package->GetProperty(::AppInstaller::Repository::PackageProperty::Name)); + } + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::InstalledVersion() + { + std::call_once(m_installedVersionOnceFlag, + [&]() + { + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> installedVersion = GetInstalledVersion(m_package); + if (installedVersion) + { + auto installedVersionImpl = winrt::make_self>(); + installedVersionImpl->Initialize(std::move(installedVersion)); + m_installedVersion = *installedVersionImpl; + } + }); + return m_installedVersion; + } + Windows::Foundation::Collections::IVectorView CatalogPackage::AvailableVersions() + { + std::call_once(m_availableVersionsOnceFlag, + [&]() + { + // Vector hasn't been populated yet. + for (auto const& versionKey : ::AppInstaller::Repository::GetAllAvailableVersions(m_package)->GetVersionKeys()) + { + auto packageVersionId = winrt::make_self>(); + packageVersionId->Initialize(versionKey); + m_availableVersions.Append(*packageVersionId); + } + }); + return m_availableVersions.GetView(); + } + + void CatalogPackage::InitializeDefaultInstallVersion() + { + std::call_once(m_defaultInstallVersionOnceFlag, + [&]() + { + using namespace AppInstaller::Pinning; + + PinningData pinningData{ PinningData::Disposition::ReadOnly }; + auto evaluator = pinningData.CreatePinStateEvaluator(PinBehavior::ConsiderPins, GetInstalledVersion(m_package)); + + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> latestVersion = + evaluator.GetLatestAvailableVersionForPins(::AppInstaller::Repository::GetAvailableVersionsForInstalledVersion(m_package)); + if (latestVersion) + { + m_updateAvailable = evaluator.IsUpdate(latestVersion); + + // DefaultInstallVersion hasn't been created yet, create and populate it. + // DefaultInstallVersion is the LatestAvailableVersion of the internal package object. + auto latestVersionImpl = winrt::make_self>(); + latestVersionImpl->Initialize(std::move(latestVersion)); + m_defaultInstallVersion = *latestVersionImpl; + } + }); + } + + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::DefaultInstallVersion() + { + InitializeDefaultInstallVersion(); + return m_defaultInstallVersion; + } + + Microsoft::Management::Deployment::PackageVersionInfo CatalogPackage::GetPackageVersionInfo(Microsoft::Management::Deployment::PackageVersionId const& versionKey) + { + winrt::Microsoft::Management::Deployment::PackageVersionInfo packageVersionInfo{ nullptr }; + + ::AppInstaller::Repository::PackageVersionKey internalVersionKey(winrt::to_string(versionKey.PackageCatalogId()), winrt::to_string(versionKey.Version()), winrt::to_string(versionKey.Channel())); + std::shared_ptr<::AppInstaller::Repository::IPackageVersion> availableVersion = + ::AppInstaller::Repository::GetAllAvailableVersions(m_package)->GetVersion(internalVersionKey); + if (availableVersion) + { + auto packageVersionInfoImpl = winrt::make_self>(); + packageVersionInfoImpl->Initialize(std::move(availableVersion)); + packageVersionInfo =*packageVersionInfoImpl; + } + return packageVersionInfo; + } + + bool CatalogPackage::IsUpdateAvailable() + { + InitializeDefaultInstallVersion(); + return m_updateAvailable; + } + Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( Microsoft::Management::Deployment::InstalledStatusType checkTypes) { co_return CheckInstalledStatus(checkTypes); - } + } Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus( Microsoft::Management::Deployment::InstalledStatusType checkTypes) { @@ -152,17 +155,17 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::implementation::CheckInstalledStatusResult>>(); checkInstalledStatusResult->Initialize(status, installedStatus); return *checkInstalledStatusResult; - } + } winrt::Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync() { co_return CheckInstalledStatus(InstalledStatusType::AllChecks); - } + } winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus() { return CheckInstalledStatus(InstalledStatusType::AllChecks); - } - std::shared_ptr<::AppInstaller::Repository::IPackage> CatalogPackage::GetRepositoryPackage() - { - return m_package; - } -} + } + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> CatalogPackage::GetRepositoryPackage() + { + return m_package; + } +} diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h index 671b4ab34f..ad06f0787a 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.h +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -12,8 +12,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( ::AppInstaller::Repository::Source source, - std::shared_ptr<::AppInstaller::Repository::IPackage> package); - std::shared_ptr<::AppInstaller::Repository::IPackage> GetRepositoryPackage(); + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> package); + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> GetRepositoryPackage(); #endif hstring Id(); @@ -34,7 +34,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: ::AppInstaller::Repository::Source m_source; - std::shared_ptr<::AppInstaller::Repository::IPackage> m_package; + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> m_package; bool m_updateAvailable = false; Windows::Foundation::Collections::IVector m_availableVersions{ winrt::single_threaded_vector() }; winrt::Microsoft::Management::Deployment::PackageVersionInfo m_installedVersion{ nullptr }; diff --git a/src/Microsoft.Management.Deployment/InstalledStatus.h b/src/Microsoft.Management.Deployment/InstalledStatus.h index f0f86d3637..b6489e9197 100644 --- a/src/Microsoft.Management.Deployment/InstalledStatus.h +++ b/src/Microsoft.Management.Deployment/InstalledStatus.h @@ -3,6 +3,7 @@ #pragma once #include "InstalledStatus.g.h" #include +#include namespace winrt::Microsoft::Management::Deployment::implementation { diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h index 471a03b3a8..bbccb81b33 100644 --- a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h +++ b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h @@ -3,6 +3,7 @@ #pragma once #include "PackageInstallerInstalledStatus.g.h" #include +#include #include namespace winrt::Microsoft::Management::Deployment::implementation diff --git a/src/Microsoft.Management.Deployment/PackageManager.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp index efbb8e2cc3..20c5dfb18f 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.cpp +++ b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -676,7 +676,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation // Add Package which is used by RecordUninstall later for removing from tracking catalog of correlated available sources as best effort winrt::Microsoft::Management::Deployment::implementation::CatalogPackage* catalogPackageImpl = get_self(package); - std::shared_ptr<::AppInstaller::Repository::IPackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); + std::shared_ptr<::AppInstaller::Repository::ICompositePackage> internalPackage = catalogPackageImpl->GetRepositoryPackage(); comContext->Add(internalPackage); return Execution::OrchestratorQueueItemFactory::CreateItemForUninstall(std::wstring{ package.Id() }, std::wstring{ package.InstalledVersion().PackageCatalog().Info().Id() }, std::move(comContext));