PredefinedInstalledSource.cpp (16639B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "TestCommon.h" 5 #include <ISource.h> 6 #include <AppInstallerRuntime.h> 7 #include <AppInstallerStrings.h> 8 #include <Microsoft/PredefinedInstalledSourceFactory.h> 9 #include <Microsoft/ARPHelper.h> 10 #include <Microsoft/SQLiteIndexSource.h> 11 12 using namespace std::string_literals; 13 using namespace std::string_view_literals; 14 using namespace TestCommon; 15 using namespace AppInstaller; 16 using namespace AppInstaller::Manifest; 17 using namespace AppInstaller::Repository; 18 using namespace AppInstaller::Runtime; 19 using namespace AppInstaller::Utility; 20 21 using SQLiteIndex = AppInstaller::Repository::Microsoft::SQLiteIndex; 22 using SQLiteIndexSource = AppInstaller::Repository::Microsoft::SQLiteIndexSource; 23 using Factory = AppInstaller::Repository::Microsoft::PredefinedInstalledSourceFactory; 24 using ARPHelper = AppInstaller::Repository::Microsoft::ARPHelper; 25 26 constexpr std::string_view s_TestScope = "TestScope"sv; 27 28 struct ARPEntry 29 { 30 ARPEntry(std::string entryName) : EntryName(std::move(entryName)) {} 31 ARPEntry(std::string entryName, std::optional<std::string> displayName, std::optional<std::string> displayVersion, bool systemComponent = false) : 32 EntryName(std::move(entryName)), DisplayName(std::move(displayName)), DisplayVersion(std::move(displayVersion)), SystemComponent(systemComponent) {} 33 34 std::string EntryName; 35 std::optional<std::string> DisplayName; 36 std::optional<std::string> DisplayVersion; 37 std::optional<std::string> Publisher; 38 std::optional<std::string> InstallLocation; 39 std::optional<std::string> UninstallString; 40 std::optional<std::string> QuietUninstallString; 41 std::optional<bool> WindowsInstaller; 42 std::optional<bool> SystemComponent; 43 }; 44 45 void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional<std::string>& value) 46 { 47 if (value) 48 { 49 SetRegistryValue(key, name, ConvertToUTF16(value.value())); 50 } 51 } 52 53 void AddARPValueToKey(HKEY key, const std::wstring& name, const std::optional<bool>& value) 54 { 55 if (value) 56 { 57 SetRegistryValue(key, name, (value.value() ? 1 : 0)); 58 } 59 } 60 61 void AddARPEntryToKey(HKEY key, const ARPHelper& helper, const ARPEntry& entry) 62 { 63 auto subkey = RegCreateVolatileSubKey(key, ConvertToUTF16(entry.EntryName)); 64 65 #define ADD_ARP_VALUE(_name_) AddARPValueToKey(subkey.get(), helper._name_, entry._name_) 66 ADD_ARP_VALUE(DisplayName); 67 ADD_ARP_VALUE(DisplayVersion); 68 ADD_ARP_VALUE(Publisher); 69 ADD_ARP_VALUE(InstallLocation); 70 ADD_ARP_VALUE(UninstallString); 71 ADD_ARP_VALUE(QuietUninstallString); 72 ADD_ARP_VALUE(WindowsInstaller); 73 ADD_ARP_VALUE(SystemComponent); 74 #undef ADD_ARP_VALUE 75 } 76 77 void AddARPEntriesToKey(HKEY key, const ARPHelper& helper, const std::vector<ARPEntry>& entries) 78 { 79 for (const auto& entry : entries) 80 { 81 AddARPEntryToKey(key, helper, entry); 82 } 83 } 84 85 SQLiteIndex::MetadataResult::const_iterator Find(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata value) 86 { 87 return std::find_if(metadata.begin(), metadata.end(), [value](const auto& m) { return m.first == value; }); 88 } 89 90 void VerifyInstalledType(const SQLiteIndex::MetadataResult& metadata, InstallerTypeEnum type) 91 { 92 auto itr = Find(metadata, PackageVersionMetadata::InstalledType); 93 REQUIRE(itr != metadata.end()); 94 REQUIRE(ConvertToInstallerTypeEnum(itr->second) == type); 95 } 96 97 void VerifyTestScope(const SQLiteIndex::MetadataResult& metadata) 98 { 99 auto itr = Find(metadata, PackageVersionMetadata::InstalledScope); 100 REQUIRE(itr != metadata.end()); 101 REQUIRE(itr->second == s_TestScope); 102 } 103 104 void VerifyMetadataString(const SQLiteIndex::MetadataResult& metadata, PackageVersionMetadata pvm, const std::optional<std::string>& value) 105 { 106 auto itr = Find(metadata, pvm); 107 if (value) 108 { 109 REQUIRE(itr != metadata.end()); 110 REQUIRE(itr->second == value.value()); 111 } 112 else 113 { 114 REQUIRE(itr == metadata.end()); 115 } 116 } 117 118 void VerifyEntryAgainstIndex(const SQLiteIndex& index, SQLiteIndex::IdType manifestId, const ARPEntry& entry) 119 { 120 REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Name) == entry.DisplayName); 121 REQUIRE(index.GetPropertyByPrimaryId(manifestId, PackageVersionProperty::Version) == entry.DisplayVersion); 122 123 REQUIRE(index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::PackageFamilyName).empty()); 124 auto productCodes = index.GetMultiPropertyByPrimaryId(manifestId, PackageVersionMultiProperty::ProductCode); 125 REQUIRE(productCodes.size() == 1); 126 REQUIRE(productCodes[0] == FoldCase(static_cast<std::string_view>(entry.EntryName))); 127 128 auto metadata = index.GetMetadataByManifestId(manifestId); 129 130 VerifyInstalledType(metadata, entry.WindowsInstaller.value_or(false) ? InstallerTypeEnum::Msi : InstallerTypeEnum::Exe); 131 VerifyTestScope(metadata); 132 VerifyMetadataString(metadata, PackageVersionMetadata::Publisher, entry.Publisher); 133 VerifyMetadataString(metadata, PackageVersionMetadata::InstalledLocation, entry.InstallLocation); 134 VerifyMetadataString(metadata, PackageVersionMetadata::StandardUninstallCommand, entry.UninstallString); 135 VerifyMetadataString(metadata, PackageVersionMetadata::SilentUninstallCommand, entry.QuietUninstallString); 136 } 137 138 std::shared_ptr<ISource> CreatePredefinedInstalledSource(Factory::Filter filter = Factory::Filter::None) 139 { 140 SourceDetails details; 141 details.Type = Factory::Type(); 142 details.Arg = Factory::FilterToString(filter); 143 144 TestProgress progress; 145 146 auto factory = Factory::Create(); 147 return factory->Create(details)->Open(progress); 148 } 149 150 SQLiteIndex CreateMemoryIndex() 151 { 152 return SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLite::Version::Latest(), SQLiteIndex::CreateOptions::SupportPathless); 153 } 154 155 TEST_CASE("ARPHelper_GetARPForArchitecture", "[arphelper][list]") 156 { 157 auto systemArch = GetSystemArchitecture(); 158 159 ARPHelper helper; 160 161 auto nativeMachineKey = helper.GetARPKey(ScopeEnum::Machine, systemArch); 162 REQUIRE(nativeMachineKey); 163 } 164 165 TEST_CASE("ARPHelper_GetBoolValue_DoesNotExist", "[arphelper][list]") 166 { 167 auto root = RegCreateVolatileTestRoot(); 168 Registry::Key key(root.get()); 169 std::wstring valueName = L"TestValueName"; 170 171 ARPHelper helper; 172 173 REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); 174 } 175 176 TEST_CASE("ARPHelper_GetBoolValue_NotDword", "[arphelper][list]") 177 { 178 auto root = RegCreateVolatileTestRoot(); 179 Registry::Key key(root.get()); 180 std::wstring valueName = L"TestValueName"; 181 182 SetRegistryValue(root.get(), valueName, L"True"); 183 184 ARPHelper helper; 185 186 REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); 187 } 188 189 TEST_CASE("ARPHelper_GetBoolValue_Zero", "[arphelper][list]") 190 { 191 auto root = RegCreateVolatileTestRoot(); 192 Registry::Key key(root.get()); 193 std::wstring valueName = L"TestValueName"; 194 195 SetRegistryValue(root.get(), valueName, 0); 196 197 ARPHelper helper; 198 199 REQUIRE_FALSE(helper.GetBoolValue(key, valueName)); 200 } 201 202 TEST_CASE("ARPHelper_GetBoolValue_One", "[arphelper][list]") 203 { 204 auto root = RegCreateVolatileTestRoot(); 205 Registry::Key key(root.get()); 206 std::wstring valueName = L"TestValueName"; 207 208 SetRegistryValue(root.get(), valueName, 1); 209 210 ARPHelper helper; 211 212 REQUIRE(helper.GetBoolValue(key, valueName)); 213 } 214 215 TEST_CASE("ARPHelper_GetBoolValue_FortyTwo", "[arphelper][list]") 216 { 217 auto root = RegCreateVolatileTestRoot(); 218 Registry::Key key(root.get()); 219 std::wstring valueName = L"TestValueName"; 220 221 SetRegistryValue(root.get(), valueName, 42); 222 223 ARPHelper helper; 224 225 REQUIRE(helper.GetBoolValue(key, valueName)); 226 } 227 228 TEST_CASE("ARPHelper_DetermineVersion_DisplayVersion", "[arphelper][list]") 229 { 230 auto root = RegCreateVolatileTestRoot(); 231 Registry::Key key(root.get()); 232 233 ARPHelper helper; 234 235 SetRegistryValue(root.get(), helper.DisplayVersion, L"1.0"); 236 SetRegistryValue(root.get(), helper.Version, 0x0207002A); 237 SetRegistryValue(root.get(), helper.VersionMajor, 3); 238 SetRegistryValue(root.get(), helper.VersionMinor, 14); 239 240 auto result = helper.DetermineVersion(key); 241 REQUIRE(result == "1.0"); 242 } 243 244 TEST_CASE("ARPHelper_DetermineVersion_Version", "[arphelper][list]") 245 { 246 auto root = RegCreateVolatileTestRoot(); 247 Registry::Key key(root.get()); 248 249 ARPHelper helper; 250 251 SetRegistryValue(root.get(), helper.Version, 0x0207002A); 252 SetRegistryValue(root.get(), helper.VersionMajor, 3); 253 SetRegistryValue(root.get(), helper.VersionMinor, 14); 254 255 auto result = helper.DetermineVersion(key); 256 REQUIRE(result == "3.14"); 257 } 258 259 TEST_CASE("ARPHelper_DetermineVersion_VersionMajorMinor", "[arphelper][list]") 260 { 261 auto root = RegCreateVolatileTestRoot(); 262 Registry::Key key(root.get()); 263 264 ARPHelper helper; 265 266 SetRegistryValue(root.get(), helper.VersionMajor, 3); 267 SetRegistryValue(root.get(), helper.VersionMinor, 14); 268 269 auto result = helper.DetermineVersion(key); 270 REQUIRE(result == "3.14"); 271 } 272 273 TEST_CASE("ARPHelper_DetermineVersion_Unknown", "[arphelper][list]") 274 { 275 auto root = RegCreateVolatileTestRoot(); 276 Registry::Key key(root.get()); 277 278 ARPHelper helper; 279 280 auto result = helper.DetermineVersion(key); 281 REQUIRE(result == Version::CreateUnknown().ToString()); 282 } 283 284 TEST_CASE("ARPHelper_PopulateIndexFromKey_Single", "[arphelper][list]") 285 { 286 auto root = RegCreateVolatileTestRoot(); 287 Registry::Key key(root.get()); 288 289 ARPHelper helper; 290 291 // Create a single ARP entry under the root 292 ARPEntry entry("SingleEntry"); 293 294 entry.DisplayName = "Test Name"; 295 entry.DisplayVersion = "1.2"; 296 entry.Publisher = "Test Publisher"; 297 entry.InstallLocation = "TestLocation"; 298 entry.UninstallString = "Test Uninstall"; 299 entry.QuietUninstallString = "Test Quiet Uninstall"; 300 entry.WindowsInstaller = true; 301 302 AddARPEntryToKey(root.get(), helper, entry); 303 304 auto index = CreateMemoryIndex(); 305 helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); 306 307 auto result = index.Search({}); 308 309 REQUIRE(result.Matches.size() == 1); 310 VerifyEntryAgainstIndex(index, result.Matches[0].first, entry); 311 } 312 313 TEST_CASE("ARPHelper_PopulateIndexFromKey_SingleValid", "[arphelper][list]") 314 { 315 auto root = RegCreateVolatileTestRoot(); 316 Registry::Key key(root.get()); 317 318 ARPHelper helper; 319 320 // Create a single ARP entry under the root 321 ARPEntry entry("SingleEntry"); 322 323 entry.DisplayName = "Test Name"; 324 entry.DisplayVersion = "1.2"; 325 entry.Publisher = "Test Publisher"; 326 entry.InstallLocation = "TestLocation"; 327 entry.UninstallString = "Test Uninstall"; 328 entry.QuietUninstallString = "Test Quiet Uninstall"; 329 entry.WindowsInstaller = false; 330 331 AddARPEntryToKey(root.get(), helper, entry); 332 333 // Name and version must exist, as well as not being a system component. 334 AddARPEntriesToKey(root.get(), helper, { 335 { "ValidButIsSystemComponent", "A", "0.1", true }, 336 { "NoName", {}, "0.2" }, 337 { "Nothing" }, 338 }); 339 340 auto index = CreateMemoryIndex(); 341 helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); 342 343 auto result = index.Search({}); 344 345 REQUIRE(result.Matches.size() == 1); 346 VerifyEntryAgainstIndex(index, result.Matches[0].first, entry); 347 } 348 349 TEST_CASE("ARPHelper_PopulateIndexFromKey_Two", "[arphelper][list]") 350 { 351 auto root = RegCreateVolatileTestRoot(); 352 Registry::Key key(root.get()); 353 354 ARPHelper helper; 355 356 ARPEntry entry1("FirstEntry"); 357 entry1.DisplayName = "Test Name"; 358 entry1.DisplayVersion = "1.2"; 359 entry1.Publisher = "Test Publisher"; 360 entry1.InstallLocation = "TestLocation"; 361 entry1.UninstallString = "Test Uninstall"; 362 entry1.QuietUninstallString = "Test Quiet Uninstall"; 363 entry1.WindowsInstaller = true; 364 365 ARPEntry entry2("SecondEntry"); 366 entry2.DisplayName = "Different Test Name"; 367 entry2.DisplayVersion = "31.4"; 368 entry2.Publisher = "Different Test Publisher"; 369 entry2.InstallLocation = "DifferentTestLocation"; 370 entry2.UninstallString = "Different Test Uninstall"; 371 entry2.QuietUninstallString = "Different Test Quiet Uninstall"; 372 373 AddARPEntryToKey(root.get(), helper, entry1); 374 AddARPEntryToKey(root.get(), helper, entry2); 375 376 auto index = CreateMemoryIndex(); 377 helper.PopulateIndexFromKey(index, key, s_TestScope, "TestArchitecture"); 378 379 REQUIRE(index.Search({}).Matches.size() == 2); 380 381 SearchRequest request; 382 request.Query = RequestMatch(MatchType::Exact, entry1.EntryName); 383 auto result = index.Search(request); 384 385 REQUIRE(result.Matches.size() == 1); 386 VerifyEntryAgainstIndex(index, result.Matches[0].first, entry1); 387 388 request.Query = RequestMatch(MatchType::Exact, entry2.EntryName); 389 result = index.Search(request); 390 391 REQUIRE(result.Matches.size() == 1); 392 VerifyEntryAgainstIndex(index, result.Matches[0].first, entry2); 393 } 394 395 TEST_CASE("PredefinedInstalledSource_Create", "[installed][list]") 396 { 397 auto source = CreatePredefinedInstalledSource(); 398 } 399 400 TEST_CASE("PredefinedInstalledSource_Search", "[installed][list]") 401 { 402 auto source = CreatePredefinedInstalledSource(); 403 404 SearchRequest request; 405 406 auto results = source->Search(request); 407 408 REQUIRE_FALSE(results.Matches.empty()); 409 } 410 411 std::string GetDatabaseIdentifier(const std::shared_ptr<Repository::ISource>& source) 412 { 413 return reinterpret_cast<SQLiteIndexSource*>(source->CastTo(ISourceType::SQLiteIndexSource))->GetIndex().GetDatabaseIdentifier(); 414 } 415 416 void RequirePackagesHaveSameNames(std::shared_ptr<ISource>& source1, std::shared_ptr<ISource>& source2) 417 { 418 auto result1 = source1->Search({}); 419 REQUIRE(!result1.Matches.empty()); 420 421 // Ensure that all packages have the same name values 422 for (const auto& match : result1.Matches) 423 { 424 std::string packageId = match.Package->GetProperty(PackageProperty::Id).get(); 425 INFO(packageId); 426 427 SearchRequest id2; 428 id2.Inclusions.emplace_back(PackageMatchFilter{ PackageMatchField::Id, MatchType::CaseInsensitive, packageId }); 429 auto result2 = source2->Search(id2); 430 REQUIRE(result2.Matches.size() == 1); 431 REQUIRE(match.Package->GetProperty(PackageProperty::Name) == result2.Matches[0].Package->GetProperty(PackageProperty::Name)); 432 } 433 } 434 435 TEST_CASE("PredefinedInstalledSource_Create_Cached", "[installed][list][installed-cache]") 436 { 437 auto source1 = CreatePredefinedInstalledSource(); 438 auto source2 = CreatePredefinedInstalledSource(); 439 440 // Ensure the same identifier (which should mean the cache was not updated) 441 REQUIRE( 442 GetDatabaseIdentifier(source1) 443 == 444 GetDatabaseIdentifier(source2) 445 ); 446 447 RequirePackagesHaveSameNames(source1, source2); 448 RequirePackagesHaveSameNames(source2, source1); 449 } 450 451 TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate", "[installed][list][installed-cache]") 452 { 453 auto source1 = CreatePredefinedInstalledSource(); 454 auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate); 455 456 // Ensure different identifier (which should mean the cache was updated) 457 REQUIRE( 458 GetDatabaseIdentifier(source1) 459 != 460 GetDatabaseIdentifier(source2) 461 ); 462 463 RequirePackagesHaveSameNames(source1, source2); 464 RequirePackagesHaveSameNames(source2, source1); 465 } 466 467 TEST_CASE("PredefinedInstalledSource_Create_ForceCacheUpdate_StillCached", "[installed][list][installed-cache]") 468 { 469 auto source1 = CreatePredefinedInstalledSource(); 470 auto source2 = CreatePredefinedInstalledSource(Factory::Filter::NoneWithForcedCacheUpdate); 471 auto source3 = CreatePredefinedInstalledSource(); 472 473 CAPTURE(GetDatabaseIdentifier(source1), GetDatabaseIdentifier(source2), GetDatabaseIdentifier(source3)); 474 475 // Ensure different identifier (which should mean the cache was updated) 476 REQUIRE( 477 GetDatabaseIdentifier(source1) 478 != 479 GetDatabaseIdentifier(source2) 480 ); 481 482 // Ensure the same identifier (which should mean the cache was not updated) 483 REQUIRE( 484 GetDatabaseIdentifier(source2) 485 == 486 GetDatabaseIdentifier(source3) 487 ); 488 }