YamlManifest.cpp (125790B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "TestCommon.h" 5 #include <AppInstallerSHA256.h> 6 #include <winget/ManifestYamlParser.h> 7 #include <winget/ManifestYamlWriter.h> 8 #include <winget/Yaml.h> 9 10 using namespace TestCommon; 11 using namespace AppInstaller::Manifest; 12 using namespace AppInstaller::Manifest::YamlParser; 13 using namespace AppInstaller::Manifest::YamlWriter; 14 using namespace AppInstaller::Utility; 15 using namespace AppInstaller::YAML; 16 17 namespace 18 { 19 using MultiValue = std::vector<NormalizedString>; 20 bool operator==(const MultiValue& a, const MultiValue& b) 21 { 22 if (a.size() != b.size()) 23 { 24 return false; 25 } 26 27 for (size_t i = 0; i < a.size(); ++i) 28 { 29 if (a[i] != b[i]) 30 { 31 return false; 32 } 33 } 34 35 return true; 36 } 37 38 void ValidateError( 39 const ValidationError& error, 40 ValidationError::Level level, 41 AppInstaller::StringResource::StringId message, 42 std::string field, 43 std::string value) 44 { 45 REQUIRE(level == error.ErrorLevel); 46 REQUIRE(message == error.Message); 47 REQUIRE(field == error.Context); 48 REQUIRE(value == error.Value); 49 } 50 51 void ValidateError(const ValidationError& error, ValidationError::Level level, AppInstaller::StringResource::StringId message) 52 { 53 ValidateError(error, level, message, std::string(), std::string()); 54 } 55 56 struct ManifestExceptionMatcher : public Catch::Matchers::MatcherBase<ManifestException> 57 { 58 ManifestExceptionMatcher(std::string expectedMessage, bool expectedWarningOnly = false) : 59 m_expectedMessage(expectedMessage), m_expectedWarningOnly(expectedWarningOnly) {} 60 61 // Performs the test for this matcher 62 bool match(ManifestException const& e) const override 63 { 64 return e.GetManifestErrorMessage().find(m_expectedMessage) != std::string::npos && 65 e.IsWarningOnly() == m_expectedWarningOnly; 66 } 67 68 virtual std::string describe() const override { 69 std::ostringstream ss; 70 ss << std::boolalpha << "Expected exception message: " << m_expectedMessage << " Expected IsWarningOnly: " << m_expectedWarningOnly; 71 return ss.str(); 72 } 73 74 private: 75 std::string m_expectedMessage; 76 bool m_expectedWarningOnly; 77 }; 78 79 ManifestValidateOption GetTestManifestValidateOption( 80 bool schemaValidationOnly = false, 81 bool errorOnVerifiedPublisher = false) 82 { 83 ManifestValidateOption validateOption; 84 validateOption.FullValidation = true; 85 validateOption.ThrowOnWarning = true; 86 validateOption.SchemaValidationOnly = schemaValidationOnly; 87 validateOption.ErrorOnVerifiedPublisherFields = errorOnVerifiedPublisher; 88 return validateOption; 89 } 90 91 void TestManifest( 92 const std::filesystem::path& manifestPath, 93 const std::string& expectedMessage = {}, 94 bool expectedWarningOnly = false, 95 ManifestValidateOption validateOption = GetTestManifestValidateOption()) 96 { 97 INFO(manifestPath.u8string()); 98 99 if (expectedMessage.empty()) 100 { 101 CHECK_NOTHROW(YamlParser::CreateFromPath(TestDataFile(manifestPath), validateOption)); 102 } 103 else 104 { 105 CHECK_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile(manifestPath), validateOption), ManifestException, ManifestExceptionMatcher(expectedMessage, expectedWarningOnly)); 106 } 107 } 108 109 struct ManifestTestCase 110 { 111 std::string TestFile; 112 std::string ExpectedMessage = {}; 113 bool IsWarningOnly = false; 114 ManifestValidateOption ValidateOption = GetTestManifestValidateOption(); 115 }; 116 117 void CopyTestDataFilesToFolder(const std::vector<std::string>& testDataFiles, const std::filesystem::path& dest) 118 { 119 for (const auto& fileName : testDataFiles) 120 { 121 std::filesystem::copy(TestDataFile(fileName), dest); 122 } 123 } 124 125 void VerifyV1ManifestContent(const Manifest& manifest, bool isSingleton, ManifestVer manifestVer = { s_ManifestVersionV1 }, bool isExported = false) 126 { 127 REQUIRE(manifest.Id == "microsoft.msixsdk"); 128 REQUIRE(manifest.Version == "1.7.32"); 129 REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); 130 REQUIRE(manifest.DefaultLocalization.Get<Localization::Publisher>() == "Microsoft"); 131 REQUIRE(manifest.DefaultLocalization.Get<Localization::PublisherUrl>() == "https://www.microsoft.com"); 132 REQUIRE(manifest.DefaultLocalization.Get<Localization::PublisherSupportUrl>() == "https://www.microsoft.com/support"); 133 REQUIRE(manifest.DefaultLocalization.Get<Localization::PrivacyUrl>() == "https://www.microsoft.com/privacy"); 134 REQUIRE(manifest.DefaultLocalization.Get<Localization::Author>() == "Microsoft"); 135 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "MSIX SDK"); 136 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageUrl>() == "https://www.microsoft.com/msixsdk/home"); 137 REQUIRE(manifest.DefaultLocalization.Get<Localization::License>() == "MIT License"); 138 REQUIRE(manifest.DefaultLocalization.Get<Localization::LicenseUrl>() == "https://www.microsoft.com/msixsdk/license"); 139 REQUIRE(manifest.DefaultLocalization.Get<Localization::Copyright>() == "Copyright Microsoft Corporation"); 140 REQUIRE(manifest.DefaultLocalization.Get<Localization::CopyrightUrl>() == "https://www.microsoft.com/msixsdk/copyright"); 141 REQUIRE(manifest.DefaultLocalization.Get<Localization::ShortDescription>() == "This is MSIX SDK"); 142 REQUIRE(manifest.DefaultLocalization.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers"); 143 REQUIRE(manifest.Moniker == "msixsdk"); 144 REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>() == MultiValue{ "appxsdk", "msixsdk" }); 145 146 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) 147 { 148 REQUIRE(manifest.DefaultLocalization.Get<Localization::ReleaseNotes>() == "Default release notes"); 149 REQUIRE(manifest.DefaultLocalization.Get<Localization::ReleaseNotesUrl>() == "https://DefaultReleaseNotes.net"); 150 REQUIRE(manifest.DefaultLocalization.Get<Localization::Agreements>().size() == 1); 151 REQUIRE(manifest.DefaultLocalization.Get<Localization::Agreements>().at(0).Label == "DefaultLabel"); 152 REQUIRE(manifest.DefaultLocalization.Get<Localization::Agreements>().at(0).AgreementText == "DefaultText"); 153 REQUIRE(manifest.DefaultLocalization.Get<Localization::Agreements>().at(0).AgreementUrl == "https://DefaultAgreementUrl.net"); 154 } 155 156 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) 157 { 158 REQUIRE(manifest.DefaultLocalization.Get<Localization::PurchaseUrl>() == "https://DefaultPurchaseUrl.com"); 159 REQUIRE(manifest.DefaultLocalization.Get<Localization::InstallationNotes>() == "Default installation notes"); 160 REQUIRE(manifest.DefaultLocalization.Get<Localization::Documentations>().size() == 1); 161 REQUIRE(manifest.DefaultLocalization.Get<Localization::Documentations>().at(0).DocumentLabel == "Default document label"); 162 REQUIRE(manifest.DefaultLocalization.Get<Localization::Documentations>().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); 163 } 164 165 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) 166 { 167 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().size() == 1); 168 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Url == "https://testIcon-en-US"); 169 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Ico); 170 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Custom); 171 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Default); 172 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); 173 } 174 175 if (!isExported) 176 { 177 REQUIRE(manifest.DefaultInstallerInfo.Locale == "en-US"); 178 REQUIRE(manifest.DefaultInstallerInfo.Platform == std::vector<PlatformEnum>{ PlatformEnum::Desktop, PlatformEnum::Universal }); 179 REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "10.0.0.0"); 180 REQUIRE(manifest.DefaultInstallerInfo.BaseInstallerType == InstallerTypeEnum::Exe); 181 REQUIRE(manifest.DefaultInstallerInfo.Scope == ScopeEnum::Machine); 182 REQUIRE(manifest.DefaultInstallerInfo.InstallModes == std::vector<InstallModeEnum>{ InstallModeEnum::Interactive, InstallModeEnum::Silent, InstallModeEnum::SilentWithProgress }); 183 184 auto defaultSwitches = manifest.DefaultInstallerInfo.Switches; 185 REQUIRE(defaultSwitches.at(InstallerSwitchType::Custom) == "/custom"); 186 REQUIRE(defaultSwitches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); 187 REQUIRE(defaultSwitches.at(InstallerSwitchType::Silent) == "/silence"); 188 REQUIRE(defaultSwitches.at(InstallerSwitchType::Interactive) == "/interactive"); 189 REQUIRE(defaultSwitches.at(InstallerSwitchType::Log) == "/log=<LOGPATH>"); 190 REQUIRE(defaultSwitches.at(InstallerSwitchType::InstallLocation) == "/dir=<INSTALLPATH>"); 191 REQUIRE(defaultSwitches.at(InstallerSwitchType::Update) == "/upgrade"); 192 193 REQUIRE(manifest.DefaultInstallerInfo.InstallerSuccessCodes == std::vector<DWORD>{ 1, static_cast<DWORD>(0x80070005) }); 194 REQUIRE(manifest.DefaultInstallerInfo.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); 195 REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); 196 REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); 197 REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "msix", "appxbundle", "msixbundle" }); 198 199 auto dependencies = manifest.DefaultInstallerInfo.Dependencies; 200 REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsFeature, "IIS")); 201 REQUIRE(dependencies.HasExactDependency(DependencyType::WindowsLibrary, "VC Runtime")); 202 REQUIRE(dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDep", "1.0.0")); 203 REQUIRE(dependencies.HasExactDependency(DependencyType::External, "Outside dependencies")); 204 REQUIRE(dependencies.Size() == 4); 205 206 REQUIRE(manifest.DefaultInstallerInfo.Capabilities == MultiValue{ "internetClient" }); 207 REQUIRE(manifest.DefaultInstallerInfo.RestrictedCapabilities == MultiValue{ "runFullTrust" }); 208 REQUIRE(manifest.DefaultInstallerInfo.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); 209 REQUIRE(manifest.DefaultInstallerInfo.ProductCode == "{Foo}"); 210 211 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) 212 { 213 REQUIRE(manifest.DefaultInstallerInfo.ReleaseDate == "2021-01-01"); 214 REQUIRE(manifest.DefaultInstallerInfo.InstallerAbortsTerminal); 215 REQUIRE(manifest.DefaultInstallerInfo.InstallLocationRequired); 216 REQUIRE(manifest.DefaultInstallerInfo.RequireExplicitUpgrade); 217 REQUIRE(manifest.DefaultInstallerInfo.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); 218 REQUIRE(manifest.DefaultInstallerInfo.UnsupportedOSArchitectures.size() == 1); 219 REQUIRE(manifest.DefaultInstallerInfo.UnsupportedOSArchitectures.at(0) == Architecture::Arm); 220 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.size() == 1); 221 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); 222 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); 223 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); 224 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); 225 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); 226 REQUIRE(manifest.DefaultInstallerInfo.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); 227 REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.size() == 1); 228 REQUIRE(manifest.DefaultInstallerInfo.Markets.AllowedMarkets.at(0) == "US"); 229 REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.size() == 1); 230 REQUIRE(manifest.DefaultInstallerInfo.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); 231 } 232 233 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) 234 { 235 REQUIRE(manifest.DefaultInstallerInfo.DisplayInstallWarnings); 236 REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.size() == 1); 237 REQUIRE(manifest.DefaultInstallerInfo.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); 238 } 239 240 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) 241 { 242 REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerType == InstallerTypeEnum::Msi); 243 REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.size() == 1); 244 REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).RelativeFilePath == "RelativeFilePath"); 245 REQUIRE(manifest.DefaultInstallerInfo.NestedInstallerFiles.at(0).PortableCommandAlias == "PortableCommandAlias"); 246 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); 247 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.size() == 1); 248 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); 249 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); 250 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 251 REQUIRE(manifest.DefaultInstallerInfo.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); 252 } 253 254 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) 255 { 256 REQUIRE(manifest.DefaultInstallerInfo.DownloadCommandProhibited); 257 } 258 259 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) 260 { 261 REQUIRE(defaultSwitches.at(InstallerSwitchType::Repair) == "/repair"); 262 REQUIRE(manifest.DefaultInstallerInfo.RepairBehavior == RepairBehaviorEnum::Modify); 263 } 264 265 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) 266 { 267 REQUIRE(manifest.DefaultInstallerInfo.ArchiveBinariesDependOnPath); 268 } 269 } 270 271 if (isSingleton || isExported) 272 { 273 REQUIRE(manifest.Installers.size() == 1); 274 } 275 else 276 { 277 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_12 }) 278 { 279 REQUIRE(manifest.Installers.size() == 7); 280 } 281 else if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) 282 { 283 REQUIRE(manifest.Installers.size() == 5); 284 } 285 else if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) 286 { 287 REQUIRE(manifest.Installers.size() == 4); 288 } 289 else if (manifestVer == ManifestVer{ s_ManifestVersionV1_2 }) 290 { 291 REQUIRE(manifest.Installers.size() == 3); 292 } 293 else 294 { 295 REQUIRE(manifest.Installers.size() == 2); 296 } 297 } 298 299 ManifestInstaller installer1 = manifest.Installers.at(0); 300 REQUIRE(installer1.Arch == Architecture::X86); 301 REQUIRE(installer1.Locale == "en-GB"); 302 REQUIRE(installer1.Platform == std::vector<PlatformEnum>{ PlatformEnum::Desktop }); 303 REQUIRE(installer1.MinOSVersion == "10.0.1.0"); 304 REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Msix); 305 REQUIRE(installer1.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.msix"); 306 REQUIRE(installer1.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 307 REQUIRE(installer1.SignatureSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 308 REQUIRE(installer1.Scope == ScopeEnum::User); 309 REQUIRE(installer1.InstallModes == std::vector<InstallModeEnum>{ InstallModeEnum::Interactive }); 310 311 auto installer1Switches = installer1.Switches; 312 REQUIRE(installer1Switches.at(InstallerSwitchType::Custom) == "/c"); 313 REQUIRE(installer1Switches.at(InstallerSwitchType::SilentWithProgress) == "/sp"); 314 REQUIRE(installer1Switches.at(InstallerSwitchType::Silent) == "/s"); 315 REQUIRE(installer1Switches.at(InstallerSwitchType::Interactive) == "/i"); 316 REQUIRE(installer1Switches.at(InstallerSwitchType::Log) == "/l=<LOGPATH>"); 317 REQUIRE(installer1Switches.at(InstallerSwitchType::InstallLocation) == "/d=<INSTALLPATH>"); 318 REQUIRE(installer1Switches.at(InstallerSwitchType::Update) == "/u"); 319 320 REQUIRE(installer1.UpdateBehavior == UpdateBehaviorEnum::Install); 321 REQUIRE(installer1.Commands == MultiValue{ "makemsixPreview", "makeappxPreview" }); 322 REQUIRE(installer1.Protocols == MultiValue{ "protocol1preview", "protocol2preview" }); 323 REQUIRE(installer1.FileExtensions == MultiValue{ "appxbundle", "msixbundle", "appx", "msix" }); 324 325 auto installer1Dependencies = installer1.Dependencies; 326 REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsFeature, "PreviewIIS")); 327 REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::WindowsLibrary, "Preview VC Runtime")); 328 REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::Package, "Microsoft.MsixSdkDepPreview", "1.0.0")); 329 REQUIRE(installer1Dependencies.HasExactDependency(DependencyType::External, "Preview Outside dependencies")); 330 REQUIRE(installer1Dependencies.Size() == 4); 331 332 REQUIRE(installer1.Capabilities == MultiValue{ "internetClientPreview" }); 333 REQUIRE(installer1.RestrictedCapabilities == MultiValue{ "runFullTrustPreview" }); 334 REQUIRE(installer1.PackageFamilyName == "Microsoft.DesktopAppInstallerPreview_8wekyb3d8bbwe"); 335 336 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) 337 { 338 REQUIRE(installer1.ReleaseDate == "2021-02-02"); 339 REQUIRE_FALSE(installer1.InstallerAbortsTerminal); 340 REQUIRE_FALSE(installer1.InstallLocationRequired); 341 REQUIRE_FALSE(installer1.RequireExplicitUpgrade); 342 REQUIRE(installer1.ElevationRequirement == ElevationRequirementEnum::ElevationRequired); 343 REQUIRE(installer1.UnsupportedOSArchitectures.size() == 1); 344 REQUIRE(installer1.UnsupportedOSArchitectures.at(0) == Architecture::Arm64); 345 REQUIRE(installer1.AppsAndFeaturesEntries.size() == 0); 346 REQUIRE(installer1.Markets.AllowedMarkets.size() == 0); 347 REQUIRE(installer1.Markets.ExcludedMarkets.size() == 1); 348 REQUIRE(installer1.Markets.ExcludedMarkets.at(0) == "US"); 349 REQUIRE(installer1.ExpectedReturnCodes.at(2).ReturnResponseEnum == ExpectedReturnCodeEnum::ContactSupport); 350 } 351 352 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) 353 { 354 REQUIRE_FALSE(installer1.DisplayInstallWarnings); 355 REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); 356 REQUIRE(installer1.ExpectedReturnCodes.at(3).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); 357 REQUIRE(installer1.UnsupportedArguments.size() == 1); 358 REQUIRE(installer1.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Location); 359 } 360 361 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) 362 { 363 // NestedInstaller metadata should not be populated unless the InstallerType is zip. 364 REQUIRE(installer1.NestedInstallerType == InstallerTypeEnum::Unknown); 365 REQUIRE(installer1.NestedInstallerFiles.size() == 0); 366 367 REQUIRE(installer1.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp"); 368 REQUIRE(installer1.InstallationMetadata.Files.size() == 1); 369 REQUIRE(installer1.InstallationMetadata.Files.at(0).RelativeFilePath == "main.exe"); 370 REQUIRE(installer1.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Launch); 371 REQUIRE(installer1.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 372 REQUIRE(installer1.InstallationMetadata.Files.at(0).InvocationParameter == "/arg"); 373 REQUIRE(installer1.InstallationMetadata.Files.at(0).DisplayName == "DisplayName"); 374 } 375 376 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) 377 { 378 REQUIRE_FALSE(installer1.DownloadCommandProhibited); 379 } 380 381 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) 382 { 383 REQUIRE(installer1.Switches.at(InstallerSwitchType::Repair) == "/r"); 384 REQUIRE(installer1.RepairBehavior == RepairBehaviorEnum::Modify); 385 } 386 387 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) 388 { 389 REQUIRE_FALSE(installer1.ArchiveBinariesDependOnPath); 390 } 391 392 if (!isSingleton) 393 { 394 if (!isExported) 395 { 396 ManifestInstaller installer2 = manifest.Installers.at(1); 397 REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Exe); 398 REQUIRE(installer2.Arch == Architecture::X64); 399 REQUIRE(installer2.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); 400 REQUIRE(installer2.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 401 REQUIRE(installer2.ProductCode == "{Bar}"); 402 403 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) 404 { 405 REQUIRE(installer2.ReleaseDate == "2021-01-01"); 406 REQUIRE(installer2.InstallerAbortsTerminal); 407 REQUIRE(installer2.InstallLocationRequired); 408 REQUIRE(installer2.RequireExplicitUpgrade); 409 REQUIRE(installer2.ElevationRequirement == ElevationRequirementEnum::ElevatesSelf); 410 REQUIRE(installer2.UnsupportedOSArchitectures.size() == 1); 411 REQUIRE(installer2.UnsupportedOSArchitectures.at(0) == Architecture::Arm); 412 REQUIRE(installer2.AppsAndFeaturesEntries.size() == 1); 413 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).DisplayName == "DisplayName"); 414 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).DisplayVersion == "DisplayVersion"); 415 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).Publisher == "Publisher"); 416 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).ProductCode == "ProductCode"); 417 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).UpgradeCode == "UpgradeCode"); 418 REQUIRE(installer2.AppsAndFeaturesEntries.at(0).InstallerType == InstallerTypeEnum::Exe); 419 REQUIRE(installer2.Markets.AllowedMarkets.size() == 1); 420 REQUIRE(installer2.Markets.AllowedMarkets.at(0) == "US"); 421 REQUIRE(installer2.ExpectedReturnCodes.size() == 1); 422 REQUIRE(installer2.ExpectedReturnCodes.at(10).ReturnResponseEnum == ExpectedReturnCodeEnum::PackageInUse); 423 } 424 425 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) 426 { 427 ManifestInstaller installer3 = manifest.Installers.at(2); 428 REQUIRE(installer3.BaseInstallerType == InstallerTypeEnum::Portable); 429 REQUIRE(installer3.Arch == Architecture::X86); 430 REQUIRE(installer3.Url == "https://www.microsoft.com/msixsdk/msixsdkx86.exe"); 431 REQUIRE(installer3.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 432 REQUIRE(installer3.Commands == MultiValue{ "standalone" }); 433 REQUIRE(installer3.ExpectedReturnCodes.size() == 1); 434 REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseEnum == ExpectedReturnCodeEnum::Custom); 435 REQUIRE(installer3.ExpectedReturnCodes.at(11).ReturnResponseUrl == "https://defaultReturnResponseUrl.com"); 436 REQUIRE_FALSE(installer3.DisplayInstallWarnings); 437 REQUIRE(installer3.UnsupportedArguments.size() == 1); 438 REQUIRE(installer3.UnsupportedArguments.at(0) == UnsupportedArgumentEnum::Log); 439 } 440 441 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_4 }) 442 { 443 ManifestInstaller installer4 = manifest.Installers.at(3); 444 REQUIRE(installer4.BaseInstallerType == InstallerTypeEnum::Zip); 445 REQUIRE(installer4.Arch == Architecture::X64); 446 REQUIRE(installer4.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); 447 REQUIRE(installer4.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 448 REQUIRE(installer4.ProductCode == "{Foo}"); 449 REQUIRE(installer4.NestedInstallerType == InstallerTypeEnum::Portable); 450 REQUIRE(installer4.NestedInstallerFiles.size() == 2); 451 REQUIRE(installer4.NestedInstallerFiles.at(0).RelativeFilePath == "relativeFilePath1"); 452 REQUIRE(installer4.NestedInstallerFiles.at(0).PortableCommandAlias == "portableAlias1"); 453 REQUIRE(installer4.NestedInstallerFiles.at(1).RelativeFilePath == "relativeFilePath2"); 454 REQUIRE(installer4.NestedInstallerFiles.at(1).PortableCommandAlias == "portableAlias2"); 455 REQUIRE(installer4.InstallationMetadata.DefaultInstallLocation == "%ProgramFiles%\\TestApp2"); 456 REQUIRE(installer4.InstallationMetadata.Files.size() == 1); 457 REQUIRE(installer4.InstallationMetadata.Files.at(0).RelativeFilePath == "main2.exe"); 458 REQUIRE(installer4.InstallationMetadata.Files.at(0).FileType == InstalledFileTypeEnum::Other); 459 REQUIRE(installer4.InstallationMetadata.Files.at(0).FileSha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 460 REQUIRE(installer4.InstallationMetadata.Files.at(0).InvocationParameter == "/arg2"); 461 REQUIRE(installer4.InstallationMetadata.Files.at(0).DisplayName == "DisplayName2"); 462 } 463 464 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_6 }) 465 { 466 REQUIRE(installer2.DownloadCommandProhibited); 467 REQUIRE(installer2.UpdateBehavior == UpdateBehaviorEnum::Deny); 468 } 469 470 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_7 }) 471 { 472 REQUIRE(installer2.RepairBehavior == RepairBehaviorEnum::Uninstaller); 473 REQUIRE(installer2.Switches.at(InstallerSwitchType::Repair) == "/r"); 474 475 ManifestInstaller installer5 = manifest.Installers.at(4); 476 REQUIRE(installer5.BaseInstallerType == InstallerTypeEnum::Burn); 477 REQUIRE(installer5.Arch == Architecture::X64); 478 REQUIRE(installer5.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); 479 REQUIRE(installer5.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 480 REQUIRE(installer5.ProductCode == "{Bar}"); 481 REQUIRE(installer5.Switches.at(InstallerSwitchType::Repair) == "/repair"); 482 REQUIRE(installer5.RepairBehavior == RepairBehaviorEnum::Modify); 483 } 484 485 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_9 }) 486 { 487 ManifestInstaller installer4 = manifest.Installers.at(3); 488 REQUIRE(installer4.ArchiveBinariesDependOnPath); 489 } 490 491 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_12 }) 492 { 493 ManifestInstaller installer6 = manifest.Installers.at(5); 494 REQUIRE(installer6.BaseInstallerType == InstallerTypeEnum::Zip); 495 REQUIRE(installer6.Arch == Architecture::Neutral); 496 REQUIRE(installer6.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); 497 REQUIRE(installer6.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 498 REQUIRE(installer6.NestedInstallerType == InstallerTypeEnum::Font); 499 REQUIRE(installer6.NestedInstallerFiles.size() == 5); 500 REQUIRE(installer6.NestedInstallerFiles.at(0).RelativeFilePath == "relativeFilePath1.otf"); 501 REQUIRE(installer6.NestedInstallerFiles.at(1).RelativeFilePath == "relativeFilePath2.ttf"); 502 REQUIRE(installer6.NestedInstallerFiles.at(2).RelativeFilePath == "relativeFilePath3.fnt"); 503 REQUIRE(installer6.NestedInstallerFiles.at(3).RelativeFilePath == "relativeFilePath4.ttc"); 504 REQUIRE(installer6.NestedInstallerFiles.at(4).RelativeFilePath == "relativeFilePath5.otc"); 505 506 ManifestInstaller installer7 = manifest.Installers.at(6); 507 REQUIRE(installer7.BaseInstallerType == InstallerTypeEnum::Font); 508 REQUIRE(installer7.Arch == Architecture::Neutral); 509 REQUIRE(installer7.Url == "https://www.microsoft.com/msixsdk/msixsdkx64.exe"); 510 REQUIRE(installer7.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 511 } 512 } 513 514 // Localization 515 REQUIRE(manifest.Localizations.size() == 1); 516 ManifestLocalization localization1 = manifest.Localizations.at(0); 517 REQUIRE(localization1.Locale == "en-GB"); 518 REQUIRE(localization1.Get<Localization::Publisher>() == "Microsoft UK"); 519 REQUIRE(localization1.Get<Localization::PublisherUrl>() == "https://www.microsoft.com/UK"); 520 REQUIRE(localization1.Get<Localization::PublisherSupportUrl>() == "https://www.microsoft.com/support/UK"); 521 REQUIRE(localization1.Get<Localization::PrivacyUrl>() == "https://www.microsoft.com/privacy/UK"); 522 REQUIRE(localization1.Get<Localization::Author>() == "Microsoft UK"); 523 REQUIRE(localization1.Get<Localization::PackageName>() == "MSIX SDK UK"); 524 REQUIRE(localization1.Get<Localization::PackageUrl>() == "https://www.microsoft.com/msixsdk/home/UK"); 525 REQUIRE(localization1.Get<Localization::License>() == "MIT License UK"); 526 REQUIRE(localization1.Get<Localization::LicenseUrl>() == "https://www.microsoft.com/msixsdk/license/UK"); 527 REQUIRE(localization1.Get<Localization::Copyright>() == "Copyright Microsoft Corporation UK"); 528 REQUIRE(localization1.Get<Localization::CopyrightUrl>() == "https://www.microsoft.com/msixsdk/copyright/UK"); 529 REQUIRE(localization1.Get<Localization::ShortDescription>() == "This is MSIX SDK UK"); 530 REQUIRE(localization1.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers UK"); 531 REQUIRE(localization1.Get<Localization::Tags>() == MultiValue{ "appxsdkUK", "msixsdkUK" }); 532 533 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_1 }) 534 { 535 REQUIRE(localization1.Get<Localization::ReleaseNotes>() == "Release notes"); 536 REQUIRE(localization1.Get<Localization::ReleaseNotesUrl>() == "https://ReleaseNotes.net"); 537 REQUIRE(localization1.Get<Localization::Agreements>().size() == 1); 538 REQUIRE(localization1.Get<Localization::Agreements>().at(0).Label == "Label"); 539 REQUIRE(localization1.Get<Localization::Agreements>().at(0).AgreementText == "Text"); 540 REQUIRE(localization1.Get<Localization::Agreements>().at(0).AgreementUrl == "https://AgreementUrl.net"); 541 } 542 543 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_2 }) 544 { 545 REQUIRE(localization1.Get<Localization::PurchaseUrl>() == "https://DefaultPurchaseUrl.com"); 546 REQUIRE(localization1.Get<Localization::InstallationNotes>() == "Default installation notes"); 547 REQUIRE(localization1.Get<Localization::Documentations>().size() == 1); 548 REQUIRE(localization1.Get<Localization::Documentations>().at(0).DocumentLabel == "Default document label"); 549 REQUIRE(localization1.Get<Localization::Documentations>().at(0).DocumentUrl == "https://DefaultDocumentUrl.com"); 550 } 551 552 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) 553 { 554 REQUIRE(localization1.Get<Localization::Icons>().size() == 1); 555 REQUIRE(localization1.Get<Localization::Icons>().at(0).Url == "https://localeTestIcon-en-GB"); 556 REQUIRE(localization1.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Png); 557 REQUIRE(localization1.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Square32); 558 REQUIRE(localization1.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Light); 559 REQUIRE(localization1.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); 560 } 561 } 562 } 563 564 struct ManifestShadowTestInfo 565 { 566 bool shadowDefaultLocale; 567 bool shadowEnGbLocale; 568 }; 569 570 void VerifyV1ManifestContentCreatedWithShadow(const Manifest& manifest, ManifestShadowTestInfo shadowInfo, ManifestVer manifestVer = { s_ManifestVersionV1_5 }) 571 { 572 REQUIRE(manifest.Id == "microsoft.msixsdk"); 573 REQUIRE(manifest.Version == "1.7.32"); 574 REQUIRE(manifest.Installers.size() == 1); 575 576 // Default localization 577 REQUIRE(manifest.DefaultLocalization.Locale == "en-US"); 578 REQUIRE(manifest.DefaultLocalization.Get<Localization::Publisher>() == "Microsoft"); 579 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "MSIX SDK"); 580 REQUIRE(manifest.DefaultLocalization.Get<Localization::License>() == "MIT License"); 581 REQUIRE(manifest.DefaultLocalization.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers"); 582 REQUIRE(manifest.DefaultLocalization.Get<Localization::ShortDescription>() == "This is MSIX SDK"); 583 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) 584 { 585 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().size() == 1); 586 587 if (shadowInfo.shadowDefaultLocale) 588 { 589 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Url == "https://shadowIcon-default"); 590 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Ico); 591 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Custom); 592 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Default); 593 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("1111111111111111111111111111111111111111111111111111111111111111")); 594 } 595 else 596 { 597 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().size() == 1); 598 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Url == "https://testIcon-en-US"); 599 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Ico); 600 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Custom); 601 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Default); 602 REQUIRE(manifest.DefaultLocalization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8123")); 603 } 604 } 605 606 // Localization 607 if (manifestVer >= ManifestVer{ s_ManifestVersionV1_5 }) 608 { 609 REQUIRE(manifest.Localizations.size() == 3); 610 611 bool foundEnGbLocale = false; 612 bool foundfrFrLocale = false; 613 for (auto const& localization : manifest.Localizations) 614 { 615 if (localization.Locale == "en-GB") 616 { 617 REQUIRE(localization.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers UK"); 618 if (shadowInfo.shadowEnGbLocale) 619 { 620 REQUIRE(localization.Get<Localization::Icons>().size() == 1); 621 REQUIRE(localization.Get<Localization::Icons>().at(0).Url == "https://shadowIcon-en-GB"); 622 REQUIRE(localization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Png); 623 REQUIRE(localization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Square32); 624 REQUIRE(localization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Light); 625 REQUIRE(localization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("2222222222222222222222222222222222222222222222222222222222222222")); 626 } 627 else 628 { 629 REQUIRE(localization.Get<Localization::Icons>().size() == 1); 630 REQUIRE(localization.Get<Localization::Icons>().at(0).Url == "https://localeTestIcon-en-GB"); 631 REQUIRE(localization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Png); 632 REQUIRE(localization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Square32); 633 REQUIRE(localization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Light); 634 REQUIRE(localization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8321")); 635 } 636 637 foundEnGbLocale = true; 638 } 639 else if (localization.Locale == "fr-FR") 640 { 641 REQUIRE(localization.Get<Localization::Icons>().size() == 1); 642 REQUIRE(localization.Get<Localization::Icons>().at(0).Url == "https://shadowIcon-fr-FR"); 643 REQUIRE(localization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Jpeg); 644 REQUIRE(localization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Square20); 645 REQUIRE(localization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Dark); 646 REQUIRE(localization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("3333333333333333333333333333333333333333333333333333333333333333")); 647 foundfrFrLocale = true; 648 } 649 else 650 { 651 REQUIRE(localization.Locale == "es-MX"); 652 REQUIRE(localization.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers MX"); 653 REQUIRE(localization.Get<Localization::Icons>().size() == 1); 654 REQUIRE(localization.Get<Localization::Icons>().at(0).Url == "https://localeTestIcon-es-MX"); 655 REQUIRE(localization.Get<Localization::Icons>().at(0).FileType == IconFileTypeEnum::Png); 656 REQUIRE(localization.Get<Localization::Icons>().at(0).Resolution == IconResolutionEnum::Square32); 657 REQUIRE(localization.Get<Localization::Icons>().at(0).Theme == IconThemeEnum::Light); 658 REQUIRE(localization.Get<Localization::Icons>().at(0).Sha256 == SHA256::ConvertToBytes("4444444444444444444444444444444444444444444444444444444444444444")); 659 } 660 } 661 662 REQUIRE(foundEnGbLocale); 663 REQUIRE(foundfrFrLocale); 664 } 665 } 666 } 667 668 TEST_CASE("ReadPreviewGoodManifestAndVerifyContents", "[ManifestValidation]") 669 { 670 auto manifestFile = TestDataFile("Manifest-Good.yaml"); 671 Manifest manifest = YamlParser::CreateFromPath(manifestFile); 672 673 REQUIRE(manifest.Id == "microsoft.msixsdk"); 674 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "MSIX SDK"); 675 REQUIRE(manifest.Moniker == "msixsdk"); 676 REQUIRE(manifest.Version == "1.7.32"); 677 REQUIRE(manifest.DefaultLocalization.Get<Localization::Publisher>() == "Microsoft"); 678 REQUIRE(manifest.Channel == "release"); 679 REQUIRE(manifest.DefaultLocalization.Get<Localization::Author>() == "Microsoft"); 680 REQUIRE(manifest.DefaultLocalization.Get<Localization::License>() == "MIT License"); 681 REQUIRE(manifest.DefaultLocalization.Get<Localization::LicenseUrl>() == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE"); 682 REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "0.0.0.0"); 683 REQUIRE(manifest.DefaultLocalization.Get<Localization::Description>() == "The MSIX SDK project is an effort to enable developers"); 684 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageUrl>() == "https://github.com/microsoft/msix-packaging"); 685 REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>() == MultiValue{ "msix", "appx" }); 686 REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); 687 REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); 688 REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "appxbundle", "msix", "msixbundle" }); 689 REQUIRE(manifest.DefaultInstallerInfo.BaseInstallerType == InstallerTypeEnum::Exe); 690 REQUIRE(manifest.DefaultInstallerInfo.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); 691 REQUIRE(manifest.DefaultInstallerInfo.ProductCode == "{Foo}"); 692 REQUIRE(manifest.DefaultInstallerInfo.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); 693 694 // default switches 695 auto switches = manifest.DefaultInstallerInfo.Switches; 696 REQUIRE(switches.at(InstallerSwitchType::Custom) == "/custom"); 697 REQUIRE(switches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); 698 REQUIRE(switches.at(InstallerSwitchType::Silent) == "/silence"); 699 REQUIRE(switches.at(InstallerSwitchType::Interactive) == "/interactive"); 700 REQUIRE(switches.at(InstallerSwitchType::Language) == "/en-us"); 701 REQUIRE(switches.at(InstallerSwitchType::Log) == "/log=<LOGPATH>"); 702 REQUIRE(switches.at(InstallerSwitchType::InstallLocation) == "/dir=<INSTALLPATH>"); 703 REQUIRE(switches.at(InstallerSwitchType::Update) == "/update"); 704 705 // installers 706 REQUIRE(manifest.Installers.size() == 2); 707 ManifestInstaller installer1 = manifest.Installers.at(0); 708 REQUIRE(installer1.Arch == Architecture::X86); 709 REQUIRE(installer1.Url == "https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip"); 710 REQUIRE(installer1.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82")); 711 REQUIRE(installer1.Locale == "en-US"); 712 REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Exe); 713 REQUIRE(installer1.Scope == ScopeEnum::User); 714 REQUIRE(installer1.PackageFamilyName == ""); 715 REQUIRE(installer1.ProductCode == "{Foo}"); 716 REQUIRE(installer1.UpdateBehavior == UpdateBehaviorEnum::Install); 717 718 auto installer1Switches = installer1.Switches; 719 REQUIRE(installer1Switches.at(InstallerSwitchType::Custom) == "/c"); 720 REQUIRE(installer1Switches.at(InstallerSwitchType::SilentWithProgress) == "/sp"); 721 REQUIRE(installer1Switches.at(InstallerSwitchType::Silent) == "/s"); 722 REQUIRE(installer1Switches.at(InstallerSwitchType::Interactive) == "/i"); 723 REQUIRE(installer1Switches.at(InstallerSwitchType::Language) == "/en"); 724 REQUIRE(installer1Switches.at(InstallerSwitchType::Log) == "/l=<LOGPATH>"); 725 REQUIRE(installer1Switches.at(InstallerSwitchType::InstallLocation) == "/d=<INSTALLPATH>"); 726 REQUIRE(installer1Switches.at(InstallerSwitchType::Update) == "/u"); 727 728 ManifestInstaller installer2 = manifest.Installers.at(1); 729 REQUIRE(installer2.Arch == Architecture::X64); 730 REQUIRE(installer2.Url == "https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip"); 731 REQUIRE(installer2.Sha256 == SHA256::ConvertToBytes("69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000")); 732 REQUIRE(installer2.Locale == "en-US"); 733 REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Exe); 734 REQUIRE(installer2.Scope == ScopeEnum::User); 735 REQUIRE(installer2.PackageFamilyName == ""); 736 REQUIRE(installer2.ProductCode == "{Foo}"); 737 REQUIRE(installer2.UpdateBehavior == UpdateBehaviorEnum::UninstallPrevious); 738 739 // Installer2 does not declare switches, it inherits switches from package default. 740 auto installer2Switches = installer2.Switches; 741 REQUIRE(installer2Switches.at(InstallerSwitchType::Custom) == "/custom"); 742 REQUIRE(installer2Switches.at(InstallerSwitchType::SilentWithProgress) == "/silentwithprogress"); 743 REQUIRE(installer2Switches.at(InstallerSwitchType::Silent) == "/silence"); 744 REQUIRE(installer2Switches.at(InstallerSwitchType::Interactive) == "/interactive"); 745 REQUIRE(installer2Switches.at(InstallerSwitchType::Language) == "/en-us"); 746 REQUIRE(installer2Switches.at(InstallerSwitchType::Log) == "/log=<LOGPATH>"); 747 REQUIRE(installer2Switches.at(InstallerSwitchType::InstallLocation) == "/dir=<INSTALLPATH>"); 748 REQUIRE(installer2Switches.at(InstallerSwitchType::Update) == "/update"); 749 750 // Localization 751 REQUIRE(manifest.Localizations.size() == 1); 752 ManifestLocalization localization1 = manifest.Localizations.at(0); 753 REQUIRE(localization1.Locale == "es-MX"); 754 REQUIRE(localization1.Get<Localization::Description>() == "El proyecto MSIX SDK es habilita desarrolladores de diferentes"); 755 REQUIRE(localization1.Get<Localization::PackageUrl>() == "https://github.com/microsoft/msix-packaging/es-MX"); 756 REQUIRE(localization1.Get<Localization::LicenseUrl>() == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX"); 757 758 // Stream hash 759 std::ifstream stream(manifestFile.GetPath(), std::ios_base::in | std::ios_base::binary); 760 REQUIRE(!stream.fail()); 761 auto manifestHash = SHA256::ComputeHash(stream); 762 REQUIRE(manifestHash.size() == manifest.StreamSha256.size()); 763 REQUIRE(std::equal(manifestHash.begin(), manifestHash.end(), manifest.StreamSha256.begin())); 764 } 765 766 TEST_CASE("ReadGoodManifestWithSpaces", "[ManifestValidation]") 767 { 768 Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-Spaces.yaml")); 769 770 REQUIRE(manifest.Id == "microsoft.msixsdk"); 771 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == "MSIX SDK"); 772 REQUIRE(manifest.Moniker == "msixsdk"); 773 REQUIRE(manifest.Version == "1.7.32"); 774 REQUIRE(manifest.Channel == "release"); 775 REQUIRE(manifest.DefaultInstallerInfo.MinOSVersion == "0.0.0.0"); 776 REQUIRE(manifest.DefaultLocalization.Get<Localization::Tags>() == MultiValue{ "msix", "appx" }); 777 REQUIRE(manifest.DefaultInstallerInfo.Commands == MultiValue{ "makemsix", "makeappx" }); 778 REQUIRE(manifest.DefaultInstallerInfo.Protocols == MultiValue{ "protocol1", "protocol2" }); 779 REQUIRE(manifest.DefaultInstallerInfo.FileExtensions == MultiValue{ "appx", "appxbundle", "msix", "msixbundle" }); 780 } 781 782 TEST_CASE("ReadGoodManifests", "[ManifestValidation]") 783 { 784 ManifestTestCase TestCases[] = 785 { 786 { "Manifest-Good-InstallerTypeExeRoot-Silent.yaml" }, 787 { "Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml" }, 788 { "Manifest-Good-InstallerTypeExe-Silent.yaml" }, 789 { "Manifest-Good-InstallerTypeExe-SilentRoot.yaml" }, 790 { "Manifest-Good-InstallerUniqueness-DefaultLang.yaml" }, 791 { "Manifest-Good-InstallerUniqueness-DiffLangs.yaml" }, 792 { "Manifest-Good-InstallerUniqueness-DiffScope.yaml" }, 793 { "Manifest-Good-Minimum.yaml" }, 794 { "Manifest-Good-Minimum-InstallerType.yaml" }, 795 { "Manifest-Good-Switches.yaml" }, 796 { "Manifest-Good-DefaultExpectedReturnCodeInInstallerSuccessCodes.yaml" }, 797 { "Manifest-Good-InstallerTypeZip-PortableExe.yaml" }, 798 }; 799 800 for (auto const& testCase : TestCases) 801 { 802 TestManifest(testCase.TestFile); 803 } 804 } 805 806 TEST_CASE("ReadBadManifests", "[ManifestValidation]") 807 { 808 ManifestTestCase TestCases[] = 809 { 810 { "Manifest-Bad-ArchInvalid.yaml", "Invalid field value. [Architecture]" }, 811 { "Manifest-Bad-ArchMissing.yaml", "Missing required property 'Arch'" }, 812 { "Manifest-Bad-Channel-NotSupported.yaml", "Field is not supported. [Channel]" }, 813 { "Manifest-Bad-DifferentCase-camelCase.yaml", "All field names should be PascalCased. [installerType]" }, 814 { "Manifest-Bad-DifferentCase-lower.yaml", "All field names should be PascalCased. [installertype]" }, 815 { "Manifest-Bad-DifferentCase-UPPER.yaml", "All field names should be PascalCased. [INSTALLERTYPE]" }, 816 { "Manifest-Bad-DuplicateKey.yaml", "Duplicate field found in the manifest." }, 817 { "Manifest-Bad-DuplicateKey-DifferentCase.yaml", "Duplicate field found in the manifest." }, 818 { "Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml", "Duplicate field found in the manifest." }, 819 { "Manifest-Bad-DuplicateReturnCode-ExpectedCodes.yaml", "Duplicate installer return code found." }, 820 { "Manifest-Bad-DuplicateReturnCode-SuccessCodes.yaml", "Duplicate installer return code found." }, 821 { "Manifest-Bad-DuplicateSha256.yaml", "Multiple Installer URLs found with the same InstallerSha256. Please ensure the accuracy of the URLs.", true }, 822 { "Manifest-Bad-IdInvalid.yaml", "Failed to validate against schema associated with property name 'Id'" }, 823 { "Manifest-Bad-IdMissing.yaml", "Missing required property 'Id'" }, 824 { "Manifest-Bad-InconsistentSha256.yaml", "The values of InstallerSha256 do not match for all instances of the same InstallerUrl." }, 825 { "Manifest-Bad-InstallersMissing.yaml", "Missing required property 'Installers'" }, 826 { "Manifest-Bad-InstallerTypeExe-NoSilent.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, 827 { "Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, 828 { "Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, 829 { "Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml", "Silent and SilentWithProgress switches are not specified for InstallerType exe.", true }, 830 { "Manifest-Bad-InstallerTypeInvalid.yaml", "Invalid field value. [InstallerType]" }, 831 { "Manifest-Bad-InstallerTypeMissing.yaml", "Invalid field value. [InstallerType]" }, 832 { "Manifest-Bad-InstallerTypePortable-InvalidAppsAndFeatures.yaml", "Only zero or one entry for Apps and Features may be specified for InstallerType portable." }, 833 { "Manifest-Bad-InstallerTypePortable-InvalidCommands.yaml", "Only zero or one value for Commands may be specified for InstallerType portable." }, 834 { "Manifest-Bad-InstallerTypePortable-InvalidScope.yaml", "Scope is not supported for InstallerType portable." }, 835 { "Manifest-Bad-InstallerTypeZip-DuplicateCommandAlias.yaml", "Duplicate portable command alias found." }, 836 { "Manifest-Bad-InstallerTypeZip-DuplicateRelativeFilePath.yaml", "Duplicate relative file path found." }, 837 { "Manifest-Bad-InstallerTypeZip-InvalidRelativeFilePath.yaml", "Relative file path must not point to a location outside of archive directory" }, 838 { "Manifest-Bad-InstallerTypeZip-MissingRelativeFilePath.yaml", "Required field missing. [RelativeFilePath]" }, 839 { "Manifest-Bad-InstallerTypeZip-MultipleNestedInstallers.yaml", "Only one entry for NestedInstallerFiles can be specified for non-portable InstallerTypes." }, 840 { "Manifest-Bad-InstallerTypeZip-NoNestedInstallerFile.yaml", "Required field missing. [NestedInstallerFiles]" }, 841 { "Manifest-Bad-InstallerTypeZip-NoNestedInstallerType.yaml", "Required field missing. [NestedInstallerType]" }, 842 { "Manifest-Bad-InstallerUniqueness.yaml", "Duplicate installer entry found." }, 843 { "Manifest-Bad-InstallerUniqueness-DefaultScope.yaml", "Duplicate installer entry found." }, 844 { "Manifest-Bad-InstallerUniqueness-DefaultValues.yaml", "Duplicate installer entry found." }, 845 { "Manifest-Bad-InstallerUniqueness-SameLang.yaml", "Duplicate installer entry found." }, 846 { "Manifest-Bad-LicenseMissing.yaml", "Missing required property 'License'" }, 847 { "Manifest-Bad-NameMissing.yaml", "Missing required property 'Name'" }, 848 { "Manifest-Bad-PublisherMissing.yaml", "Missing required property 'Publisher'" }, 849 { "Manifest-Bad-Sha256Invalid.yaml", "Failed to validate against schema associated with property name 'Sha256'" }, 850 { "Manifest-Bad-Sha256Missing.yaml", "Required field missing. [InstallerSha256]" }, 851 { "Manifest-Bad-SwitchInvalid.yaml", "Unknown field. [NotASwitch]", true }, 852 { "Manifest-Bad-UnknownProperty.yaml", "Unknown field. [Fake]", true }, 853 { "Manifest-Bad-UnsupportedVersion.yaml", "Unsupported ManifestVersion" }, 854 { "Manifest-Bad-UrlInvalid.yaml", "Invalid field value. [InstallerUrl]" }, 855 { "Manifest-Bad-UrlMissing.yaml", "Required field missing. [InstallerUrl]" }, 856 { "Manifest-Bad-VersionInvalid.yaml", "Failed to validate against schema associated with property name 'Version'" }, 857 { "Manifest-Bad-VersionMissing.yaml", "Missing required property 'Version'" }, 858 { "Manifest-Bad-InvalidManifestVersionValue.yaml", "Failed to validate against schema associated with property name 'ManifestVersion'" }, 859 { "InstallFlowTest_MSStore.yaml", "Field value is not supported. [InstallerType] Value: msstore" }, 860 { "Manifest-Bad-PackageFamilyNameOnMSI.yaml", "The specified installer type does not support PackageFamilyName. [InstallerType] Value: msi", true }, 861 { "Manifest-Bad-ProductCodeOnMSIX.yaml", "The specified installer type does not support ProductCode. [InstallerType] Value: msix" }, 862 { "Manifest-Bad-InvalidUpdateBehavior.yaml", "Invalid field value. [UpgradeBehavior]" }, 863 { "Manifest-Bad-InvalidLocale.yaml", "The locale value is not a well formed bcp47 language tag." }, 864 { "Manifest-Bad-AppsAndFeaturesEntriesOnMSIX.yaml", "The specified installer type does not write to Apps and Features entry." }, 865 { "InstallFlowTest_LicenseAgreement.yaml", "Field usage requires verified publishers. [Agreement]", true }, 866 { "InstallFlowTest_LicenseAgreement.yaml", "Field usage requires verified publishers. [Agreement]", false, GetTestManifestValidateOption(false, true) }, 867 { "Manifest-Bad-ApproximateVersionInPackageVersion.yaml", "Approximate version not allowed. [PackageVersion]" }, 868 { "Manifest-Bad-ApproximateVersionInArpVersion.yaml", "Approximate version not allowed. [DisplayVersion]" }, 869 { "Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml", "The file type of the referenced file is not allowed. [RelativeFilePath] Value: ScriptedApplication.bat" }, 870 { "Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml", "The file type of the referenced file is not allowed. [RelativeFilePath] Value: ScriptedApplication.bat" }, 871 }; 872 873 for (auto const& testCase : TestCases) 874 { 875 TestManifest(testCase.TestFile, testCase.ExpectedMessage, testCase.IsWarningOnly, testCase.ValidateOption); 876 } 877 } 878 879 TEST_CASE("ManifestEncoding", "[ManifestValidation]") 880 { 881 ManifestTestCase TestCases[] = 882 { 883 { "Manifest-Encoding-ANSI.yaml" }, 884 { "Manifest-Encoding-UTF8.yaml" }, 885 { "Manifest-Encoding-UTF8-BOM.yaml" }, 886 { "Manifest-Encoding-UTF16BE.yaml" }, 887 { "Manifest-Encoding-UTF16BE-BOM.yaml" }, 888 { "Manifest-Encoding-UTF16LE.yaml" }, 889 { "Manifest-Encoding-UTF16LE-BOM.yaml" }, 890 }; 891 892 for (auto const& testCase : TestCases) 893 { 894 INFO(testCase.TestFile); 895 Manifest manifest = YamlParser::CreateFromPath(TestDataFile(testCase.TestFile), GetTestManifestValidateOption()); 896 REQUIRE(manifest.DefaultLocalization.Get<Localization::PackageName>() == u8"MSIX SDK\xA9"); 897 } 898 } 899 900 TEST_CASE("ComplexSystemReference", "[ManifestValidation]") 901 { 902 Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-SystemReferenceComplex.yaml")); 903 904 REQUIRE(manifest.Installers.size() == 4); 905 906 // MSIX installer does inherit 907 auto& installer = manifest.Installers[0]; 908 REQUIRE(installer.BaseInstallerType == InstallerTypeEnum::Msix); 909 REQUIRE(installer.Arch == Architecture::X86); 910 REQUIRE(installer.PackageFamilyName == "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"); 911 REQUIRE(installer.ProductCode == ""); 912 913 // MSI installer does inherit 914 auto& installer1 = manifest.Installers[1]; 915 REQUIRE(installer1.BaseInstallerType == InstallerTypeEnum::Msi); 916 REQUIRE(installer1.Arch == Architecture::X86); 917 REQUIRE(installer1.PackageFamilyName == ""); 918 REQUIRE(installer1.ProductCode == "{Foo}"); 919 920 // MSIX installer with override 921 auto& installer2 = manifest.Installers[2]; 922 REQUIRE(installer2.BaseInstallerType == InstallerTypeEnum::Msix); 923 REQUIRE(installer2.Arch == Architecture::X64); 924 REQUIRE(installer2.PackageFamilyName == "Override_8wekyb3d8bbwe"); 925 REQUIRE(installer2.ProductCode == ""); 926 927 // MSI installer with override 928 auto& installer3 = manifest.Installers[3]; 929 REQUIRE(installer3.BaseInstallerType == InstallerTypeEnum::Msi); 930 REQUIRE(installer3.Arch == Architecture::X64); 931 REQUIRE(installer3.PackageFamilyName == ""); 932 REQUIRE(installer3.ProductCode == "Override"); 933 } 934 935 TEST_CASE("ManifestVersionExtensions", "[ManifestValidation]") 936 { 937 REQUIRE(!ManifestVer("1.0.0"sv).HasExtension("msstore")); 938 REQUIRE(!ManifestVer("1.0.0-other"sv).HasExtension("msstore")); 939 REQUIRE(!ManifestVer("1.0.0-other-other2"sv).HasExtension("msstore")); 940 941 REQUIRE(ManifestVer("1.0.0-msstore"sv).HasExtension("msstore")); 942 REQUIRE(ManifestVer("1.0.0-msstore.2"sv).HasExtension("msstore")); 943 REQUIRE(ManifestVer("1.0.0-other-msstore.2"sv).HasExtension("msstore")); 944 REQUIRE(ManifestVer("1.0.0-msstore.2-other"sv).HasExtension("msstore")); 945 } 946 947 TEST_CASE("ValidateV1GoodManifestAndVerifyContents", "[ManifestValidation]") 948 { 949 ManifestValidateOption validateOption; 950 validateOption.FullValidation = true; 951 TempDirectory singletonDirectory{ "SingletonManifest" }; 952 CopyTestDataFilesToFolder({ "ManifestV1-Singleton.yaml" }, singletonDirectory); 953 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 954 VerifyV1ManifestContent(singletonManifest, true); 955 956 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 957 CopyTestDataFilesToFolder({ 958 "ManifestV1-MultiFile-Version.yaml", 959 "ManifestV1-MultiFile-Installer.yaml", 960 "ManifestV1-MultiFile-DefaultLocale.yaml", 961 "ManifestV1-MultiFile-Locale.yaml" }, multiFileDirectory); 962 963 TempFile mergedManifestFile{ "merged.yaml" }; 964 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 965 VerifyV1ManifestContent(multiFileManifest, false); 966 967 // Read from merged manifest should have the same content as multi file manifest 968 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 969 VerifyV1ManifestContent(mergedManifest, false); 970 } 971 972 TEST_CASE("ValidateV1_1GoodManifestAndVerifyContents", "[ManifestValidation]") 973 { 974 ManifestValidateOption validateOption; 975 validateOption.FullValidation = true; 976 TempDirectory singletonDirectory{ "SingletonManifest" }; 977 CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory); 978 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 979 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_1 }); 980 981 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 982 CopyTestDataFilesToFolder({ 983 "ManifestV1_1-MultiFile-Version.yaml", 984 "ManifestV1_1-MultiFile-Installer.yaml", 985 "ManifestV1_1-MultiFile-DefaultLocale.yaml", 986 "ManifestV1_1-MultiFile-Locale.yaml" }, multiFileDirectory); 987 988 TempFile mergedManifestFile{ "merged.yaml" }; 989 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 990 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_1 }); 991 992 // Read from merged manifest should have the same content as multi file manifest 993 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 994 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_1 }); 995 } 996 997 TEST_CASE("ValidateV1_2GoodManifestAndVerifyContents", "[ManifestValidation]") 998 { 999 ManifestValidateOption validateOption; 1000 validateOption.FullValidation = true; 1001 TempDirectory singletonDirectory{ "SingletonManifest" }; 1002 CopyTestDataFilesToFolder({ "ManifestV1_2-Singleton.yaml" }, singletonDirectory); 1003 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1004 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_2 }); 1005 1006 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1007 CopyTestDataFilesToFolder({ 1008 "ManifestV1_2-MultiFile-Version.yaml", 1009 "ManifestV1_2-MultiFile-Installer.yaml", 1010 "ManifestV1_2-MultiFile-DefaultLocale.yaml", 1011 "ManifestV1_2-MultiFile-Locale.yaml" }, multiFileDirectory); 1012 1013 TempFile mergedManifestFile{ "merged.yaml" }; 1014 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1015 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_2 }); 1016 1017 // Read from merged manifest should have the same content as multi file manifest 1018 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1019 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_2 }); 1020 } 1021 1022 TEST_CASE("ValidateV1_4GoodManifestAndVerifyContents", "[ManifestValidation]") 1023 { 1024 ManifestValidateOption validateOption; 1025 validateOption.FullValidation = true; 1026 TempDirectory singletonDirectory{ "SingletonManifest" }; 1027 CopyTestDataFilesToFolder({ "ManifestV1_4-Singleton.yaml" }, singletonDirectory); 1028 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1029 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_4 }); 1030 1031 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1032 CopyTestDataFilesToFolder({ 1033 "ManifestV1_4-MultiFile-Version.yaml", 1034 "ManifestV1_4-MultiFile-Installer.yaml", 1035 "ManifestV1_4-MultiFile-DefaultLocale.yaml", 1036 "ManifestV1_4-MultiFile-Locale.yaml" }, multiFileDirectory); 1037 1038 TempFile mergedManifestFile{ "merged.yaml" }; 1039 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1040 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_4 }); 1041 1042 // Read from merged manifest should have the same content as multi file manifest 1043 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1044 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_4 }); 1045 } 1046 1047 TEST_CASE("ValidateV1_5GoodManifestAndVerifyContents", "[ManifestValidation]") 1048 { 1049 ManifestValidateOption validateOption; 1050 validateOption.FullValidation = true; 1051 TempDirectory singletonDirectory{ "SingletonManifest" }; 1052 CopyTestDataFilesToFolder({ "ManifestV1_5-Singleton.yaml" }, singletonDirectory); 1053 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1054 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_5 }); 1055 1056 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1057 CopyTestDataFilesToFolder({ 1058 "ManifestV1_5-MultiFile-Version.yaml", 1059 "ManifestV1_5-MultiFile-Installer.yaml", 1060 "ManifestV1_5-MultiFile-DefaultLocale.yaml", 1061 "ManifestV1_5-MultiFile-Locale.yaml" }, multiFileDirectory); 1062 1063 TempFile mergedManifestFile{ "merged.yaml" }; 1064 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1065 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_5 }); 1066 1067 // Read from merged manifest should have the same content as multi file manifest 1068 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1069 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_5 }); 1070 } 1071 1072 TEST_CASE("ValidateV1_6GoodManifestAndVerifyContents", "[ManifestValidation]") 1073 { 1074 ManifestValidateOption validateOption; 1075 validateOption.FullValidation = true; 1076 TempDirectory singletonDirectory{ "SingletonManifest" }; 1077 CopyTestDataFilesToFolder({ "ManifestV1_6-Singleton.yaml" }, singletonDirectory); 1078 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1079 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_6 }); 1080 1081 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1082 CopyTestDataFilesToFolder({ 1083 "ManifestV1_6-MultiFile-Version.yaml", 1084 "ManifestV1_6-MultiFile-Installer.yaml", 1085 "ManifestV1_6-MultiFile-DefaultLocale.yaml", 1086 "ManifestV1_6-MultiFile-Locale.yaml" }, multiFileDirectory); 1087 1088 TempFile mergedManifestFile{ "merged.yaml" }; 1089 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1090 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_6 }); 1091 1092 // Read from merged manifest should have the same content as multi file manifest 1093 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1094 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_6 }); 1095 } 1096 1097 TEST_CASE("ValidateV1_7GoodManifestAndVerifyContents", "[ManifestValidation]") 1098 { 1099 ManifestValidateOption validateOption; 1100 validateOption.FullValidation = true; 1101 TempDirectory singletonDirectory{ "SingletonManifest" }; 1102 CopyTestDataFilesToFolder({ "ManifestV1_7-Singleton.yaml" }, singletonDirectory); 1103 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1104 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_7 }); 1105 1106 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1107 CopyTestDataFilesToFolder({ 1108 "ManifestV1_7-MultiFile-Version.yaml", 1109 "ManifestV1_7-MultiFile-Installer.yaml", 1110 "ManifestV1_7-MultiFile-DefaultLocale.yaml", 1111 "ManifestV1_7-MultiFile-Locale.yaml" }, multiFileDirectory); 1112 1113 TempFile mergedManifestFile{ "merged.yaml" }; 1114 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1115 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_7 }); 1116 1117 // Read from merged manifest should have the same content as multi file manifest 1118 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1119 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_7 }); 1120 } 1121 1122 TEST_CASE("ValidateV1_9GoodManifestAndVerifyContents", "[ManifestValidation]") 1123 { 1124 ManifestValidateOption validateOption; 1125 validateOption.FullValidation = true; 1126 TempDirectory singletonDirectory{ "SingletonManifest" }; 1127 CopyTestDataFilesToFolder({ "ManifestV1_9-Singleton.yaml" }, singletonDirectory); 1128 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1129 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_9 }); 1130 1131 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1132 CopyTestDataFilesToFolder({ 1133 "ManifestV1_9-MultiFile-Version.yaml", 1134 "ManifestV1_9-MultiFile-Installer.yaml", 1135 "ManifestV1_9-MultiFile-DefaultLocale.yaml", 1136 "ManifestV1_9-MultiFile-Locale.yaml" }, multiFileDirectory); 1137 1138 TempFile mergedManifestFile{ "merged.yaml" }; 1139 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1140 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_9 }); 1141 1142 // Read from merged manifest should have the same content as multi file manifest 1143 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1144 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_9 }); 1145 } 1146 1147 TEST_CASE("ValidateV1_10GoodManifestAndVerifyContents", "[ManifestValidation]") 1148 { 1149 ManifestValidateOption validateOption; 1150 validateOption.FullValidation = true; 1151 TempDirectory singletonDirectory{ "SingletonManifest" }; 1152 CopyTestDataFilesToFolder({ "ManifestV1_10-Singleton.yaml" }, singletonDirectory); 1153 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1154 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_10 }); 1155 1156 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1157 CopyTestDataFilesToFolder({ 1158 "ManifestV1_10-MultiFile-Version.yaml", 1159 "ManifestV1_10-MultiFile-Installer.yaml", 1160 "ManifestV1_10-MultiFile-DefaultLocale.yaml", 1161 "ManifestV1_10-MultiFile-Locale.yaml" }, multiFileDirectory); 1162 1163 TempFile mergedManifestFile{ "merged.yaml" }; 1164 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1165 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_10 }); 1166 1167 // Read from merged manifest should have the same content as multi file manifest 1168 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1169 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_10 }); 1170 } 1171 1172 TEST_CASE("ValidateV1_12GoodManifestAndVerifyContents", "[ManifestValidation]") 1173 { 1174 ManifestValidateOption validateOption; 1175 validateOption.FullValidation = true; 1176 TempDirectory singletonDirectory{ "SingletonManifest" }; 1177 CopyTestDataFilesToFolder({ "ManifestV1_12-Singleton.yaml" }, singletonDirectory); 1178 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory, validateOption); 1179 VerifyV1ManifestContent(singletonManifest, true, ManifestVer{ s_ManifestVersionV1_12 }); 1180 1181 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1182 CopyTestDataFilesToFolder({ 1183 "ManifestV1_12-MultiFile-Version.yaml", 1184 "ManifestV1_12-MultiFile-Installer.yaml", 1185 "ManifestV1_12-MultiFile-DefaultLocale.yaml", 1186 "ManifestV1_12-MultiFile-Locale.yaml" }, multiFileDirectory); 1187 1188 TempFile mergedManifestFile{ "merged.yaml" }; 1189 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1190 VerifyV1ManifestContent(multiFileManifest, false, ManifestVer{ s_ManifestVersionV1_12 }); 1191 1192 // Read from merged manifest should have the same content as multi file manifest 1193 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1194 VerifyV1ManifestContent(mergedManifest, false, ManifestVer{ s_ManifestVersionV1_12 }); 1195 } 1196 1197 TEST_CASE("WriteV1SingletonManifestAndVerifyContents", "[ManifestCreation]") 1198 { 1199 TempDirectory singletonDirectory{ "SingletonManifest" }; 1200 CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory); 1201 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1202 1203 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1204 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1205 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1206 1207 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1208 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1209 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1 }, true); 1210 1211 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1212 CopyTestDataFilesToFolder({ 1213 "ManifestV1-MultiFile-Version.yaml", 1214 "ManifestV1-MultiFile-Installer.yaml", 1215 "ManifestV1-MultiFile-DefaultLocale.yaml", 1216 "ManifestV1-MultiFile-Locale.yaml" }, multiFileDirectory); 1217 1218 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1219 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1220 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1221 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1222 1223 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1224 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1225 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1 }, true); 1226 } 1227 1228 TEST_CASE("WriteV1_1SingletonManifestAndVerifyContents", "[ManifestCreation]") 1229 { 1230 TempDirectory singletonDirectory{ "SingletonManifest" }; 1231 CopyTestDataFilesToFolder({ "ManifestV1_1-Singleton.yaml" }, singletonDirectory); 1232 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1233 1234 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1235 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1236 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1237 1238 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1239 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1240 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_1 }, true); 1241 1242 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1243 CopyTestDataFilesToFolder({ 1244 "ManifestV1_1-MultiFile-Version.yaml", 1245 "ManifestV1_1-MultiFile-Installer.yaml", 1246 "ManifestV1_1-MultiFile-DefaultLocale.yaml", 1247 "ManifestV1_1-MultiFile-Locale.yaml" }, multiFileDirectory); 1248 1249 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1250 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1251 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1252 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1253 1254 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1255 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1256 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_1 }, true); 1257 } 1258 1259 TEST_CASE("WriteV1_2SingletonManifestAndVerifyContents", "[ManifestCreation]") 1260 { 1261 TempDirectory singletonDirectory{ "SingletonManifest" }; 1262 CopyTestDataFilesToFolder({ "ManifestV1_2-Singleton.yaml" }, singletonDirectory); 1263 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1264 1265 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1266 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1267 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1268 1269 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1270 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1271 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_2 }, true); 1272 1273 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1274 CopyTestDataFilesToFolder({ 1275 "ManifestV1_2-MultiFile-Version.yaml", 1276 "ManifestV1_2-MultiFile-Installer.yaml", 1277 "ManifestV1_2-MultiFile-DefaultLocale.yaml", 1278 "ManifestV1_2-MultiFile-Locale.yaml" }, multiFileDirectory); 1279 1280 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1281 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1282 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1283 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1284 1285 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1286 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1287 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_2 }, true); 1288 } 1289 1290 TEST_CASE("WriteV1_4SingletonManifestAndVerifyContents", "[ManifestCreation]") 1291 { 1292 TempDirectory singletonDirectory{ "SingletonManifest" }; 1293 CopyTestDataFilesToFolder({ "ManifestV1_4-Singleton.yaml" }, singletonDirectory); 1294 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1295 1296 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1297 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1298 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1299 1300 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1301 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1302 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_4 }, true); 1303 1304 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1305 CopyTestDataFilesToFolder({ 1306 "ManifestV1_4-MultiFile-Version.yaml", 1307 "ManifestV1_4-MultiFile-Installer.yaml", 1308 "ManifestV1_4-MultiFile-DefaultLocale.yaml", 1309 "ManifestV1_4-MultiFile-Locale.yaml" }, multiFileDirectory); 1310 1311 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1312 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1313 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1314 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1315 1316 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1317 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1318 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_4 }, true); 1319 } 1320 1321 TEST_CASE("WriteV1_5SingletonManifestAndVerifyContents", "[ManifestCreation]") 1322 { 1323 TempDirectory singletonDirectory{ "SingletonManifest" }; 1324 CopyTestDataFilesToFolder({ "ManifestV1_5-Singleton.yaml" }, singletonDirectory); 1325 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1326 1327 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1328 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1329 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1330 1331 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1332 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1333 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_5 }, true); 1334 1335 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1336 CopyTestDataFilesToFolder({ 1337 "ManifestV1_5-MultiFile-Version.yaml", 1338 "ManifestV1_5-MultiFile-Installer.yaml", 1339 "ManifestV1_5-MultiFile-DefaultLocale.yaml", 1340 "ManifestV1_5-MultiFile-Locale.yaml" }, multiFileDirectory); 1341 1342 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1343 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1344 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1345 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1346 1347 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1348 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1349 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_5 }, true); 1350 } 1351 1352 TEST_CASE("WriteV1_6SingletonManifestAndVerifyContents", "[ManifestCreation]") 1353 { 1354 TempDirectory singletonDirectory{ "SingletonManifest" }; 1355 CopyTestDataFilesToFolder({ "ManifestV1_6-Singleton.yaml" }, singletonDirectory); 1356 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1357 1358 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1359 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1360 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1361 1362 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1363 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1364 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_6 }, true); 1365 1366 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1367 CopyTestDataFilesToFolder({ 1368 "ManifestV1_6-MultiFile-Version.yaml", 1369 "ManifestV1_6-MultiFile-Installer.yaml", 1370 "ManifestV1_6-MultiFile-DefaultLocale.yaml", 1371 "ManifestV1_6-MultiFile-Locale.yaml" }, multiFileDirectory); 1372 1373 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1374 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1375 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1376 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1377 1378 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1379 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1380 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_6 }, true); 1381 } 1382 1383 TEST_CASE("WriteV1_7SingletonManifestAndVerifyContents", "[ManifestCreation]") 1384 { 1385 TempDirectory singletonDirectory{ "SingletonManifest" }; 1386 CopyTestDataFilesToFolder({ "ManifestV1_7-Singleton.yaml" }, singletonDirectory); 1387 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1388 1389 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1390 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1391 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1392 1393 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1394 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1395 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_7 }, true); 1396 1397 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1398 CopyTestDataFilesToFolder({ 1399 "ManifestV1_7-MultiFile-Version.yaml", 1400 "ManifestV1_7-MultiFile-Installer.yaml", 1401 "ManifestV1_7-MultiFile-DefaultLocale.yaml", 1402 "ManifestV1_7-MultiFile-Locale.yaml" }, multiFileDirectory); 1403 1404 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1405 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1406 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1407 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1408 1409 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1410 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1411 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_7 }, true); 1412 } 1413 1414 TEST_CASE("WriteV1_9SingletonManifestAndVerifyContents", "[ManifestCreation]") 1415 { 1416 TempDirectory singletonDirectory{ "SingletonManifest" }; 1417 CopyTestDataFilesToFolder({ "ManifestV1_9-Singleton.yaml" }, singletonDirectory); 1418 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1419 1420 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1421 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1422 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1423 1424 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1425 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1426 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_9 }, true); 1427 1428 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1429 CopyTestDataFilesToFolder({ 1430 "ManifestV1_9-MultiFile-Version.yaml", 1431 "ManifestV1_9-MultiFile-Installer.yaml", 1432 "ManifestV1_9-MultiFile-DefaultLocale.yaml", 1433 "ManifestV1_9-MultiFile-Locale.yaml" }, multiFileDirectory); 1434 1435 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1436 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1437 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1438 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1439 1440 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1441 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1442 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_9 }, true); 1443 } 1444 1445 TEST_CASE("WriteV1_10SingletonManifestAndVerifyContents", "[ManifestCreation]") 1446 { 1447 TempDirectory singletonDirectory{ "SingletonManifest" }; 1448 CopyTestDataFilesToFolder({ "ManifestV1_10-Singleton.yaml" }, singletonDirectory); 1449 Manifest singletonManifest = YamlParser::CreateFromPath(singletonDirectory); 1450 1451 TempDirectory exportedSingletonDirectory{ "exportedSingleton" }; 1452 std::filesystem::path generatedSingletonManifestPath = exportedSingletonDirectory.GetPath() / "testSingletonManifest.yaml"; 1453 YamlWriter::OutputYamlFile(singletonManifest, singletonManifest.Installers[0], generatedSingletonManifestPath); 1454 1455 REQUIRE(std::filesystem::exists(generatedSingletonManifestPath)); 1456 Manifest generatedSingletonManifest = YamlParser::CreateFromPath(exportedSingletonDirectory); 1457 VerifyV1ManifestContent(generatedSingletonManifest, true, ManifestVer{ s_ManifestVersionV1_10 }, true); 1458 1459 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1460 CopyTestDataFilesToFolder({ 1461 "ManifestV1_10-MultiFile-Version.yaml", 1462 "ManifestV1_10-MultiFile-Installer.yaml", 1463 "ManifestV1_10-MultiFile-DefaultLocale.yaml", 1464 "ManifestV1_10-MultiFile-Locale.yaml" }, multiFileDirectory); 1465 1466 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory); 1467 TempDirectory exportedMultiFileDirectory{ "exportedMultiFile" }; 1468 std::filesystem::path generatedMultiFileManifestPath = exportedMultiFileDirectory.GetPath() / "testMultiFileManifest.yaml"; 1469 YamlWriter::OutputYamlFile(multiFileManifest, multiFileManifest.Installers[0], generatedMultiFileManifestPath); 1470 1471 REQUIRE(std::filesystem::exists(generatedMultiFileManifestPath)); 1472 Manifest generatedMultiFileManifest = YamlParser::CreateFromPath(exportedMultiFileDirectory); 1473 VerifyV1ManifestContent(generatedMultiFileManifest, false, ManifestVer{ s_ManifestVersionV1_10 }, true); 1474 } 1475 1476 // Since Authentication is not supported in community repo and will cause manifest validation failure, 1477 // we are not adding Authentication in v1_10 manifests. Instead a separate test is created for Authentication. 1478 TEST_CASE("ReadWriteValidateV1_10ManifestWithInstallerAuthentication", "[ManifestValidation]") 1479 { 1480 // Read manifest 1481 TempDirectory testDirectory{ "TestManifest" }; 1482 CopyTestDataFilesToFolder({ "ManifestV1_10-InstallerAuthentication.yaml" }, testDirectory); 1483 Manifest testManifest = YamlParser::CreateFromPath(testDirectory); 1484 1485 // Verify content 1486 REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 }); 1487 REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraId); 1488 REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo); 1489 REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo->Resource == "TestResource"); 1490 REQUIRE(testManifest.DefaultInstallerInfo.AuthInfo.MicrosoftEntraIdInfo->Scope == "TestScope"); 1491 REQUIRE(testManifest.Installers.size() == 1); 1492 REQUIRE(testManifest.Installers[0].AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage); 1493 REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo); 1494 REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Resource == "https://storage.azure.com/"); 1495 REQUIRE(testManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Scope.empty()); 1496 1497 // Manifest Validation. Only error is "Authentication not supported". 1498 auto errors = ValidateManifest(testManifest, true); 1499 REQUIRE(errors.size() == 1); 1500 REQUIRE(errors[0].GetErrorMessage() == "Field is not supported."); 1501 REQUIRE(errors[0].Context == "Authentication"); 1502 1503 // Write manifest 1504 TempDirectory exportedDirectory{ "ExportedManifest" }; 1505 std::filesystem::path exportedManifestPath = exportedDirectory.GetPath() / "ExportedManifest.yaml"; 1506 YamlWriter::OutputYamlFile(testManifest, testManifest.Installers[0], exportedManifestPath); 1507 1508 // Read back and validate content 1509 REQUIRE(std::filesystem::exists(exportedManifestPath)); 1510 Manifest exportedManifest = YamlParser::CreateFromPath(exportedDirectory); 1511 REQUIRE(testManifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ s_ManifestVersionV1_10 }); 1512 REQUIRE(exportedManifest.Installers.size() == 1); 1513 REQUIRE(exportedManifest.Installers[0].AuthInfo.Type == AppInstaller::Authentication::AuthenticationType::MicrosoftEntraIdForAzureBlobStorage); 1514 REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo); 1515 REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Resource == "https://storage.azure.com/"); 1516 REQUIRE(exportedManifest.Installers[0].AuthInfo.MicrosoftEntraIdInfo->Scope.empty()); 1517 } 1518 1519 TEST_CASE("WriteManifestWithMultipleLocale", "[ManifestCreation]") 1520 { 1521 Manifest multiLocaleManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); 1522 TempDirectory exportedDirectory{ "exported" }; 1523 std::filesystem::path generatedManifestPath = exportedDirectory.GetPath() / "testManifestWithMultipleLocale.yaml"; 1524 YamlWriter::OutputYamlFile(multiLocaleManifest, multiLocaleManifest.Installers[0], generatedManifestPath); 1525 1526 REQUIRE(std::filesystem::exists(generatedManifestPath)); 1527 Manifest generatedManifest = YamlParser::CreateFromPath(generatedManifestPath); 1528 REQUIRE(generatedManifest.Localizations.size() == 2); 1529 } 1530 1531 TEST_CASE("WriteManifestWithMSStoreInstaller", "[ManifestCreation]") 1532 { 1533 Manifest msstoreManifest = YamlParser::CreateFromPath(TestDataFile("DownloadFlowTest_MSStore.yaml")); 1534 TempDirectory exportedDirectory{ "exported" }; 1535 std::filesystem::path generatedManifestPath = exportedDirectory.GetPath() / "testManifestWithMultipleLocale.yaml"; 1536 msstoreManifest.ManifestVersion = ManifestVer{ "1.1.0" }; 1537 YamlWriter::OutputYamlFile(msstoreManifest, msstoreManifest.Installers[0], generatedManifestPath); 1538 1539 REQUIRE(std::filesystem::exists(generatedManifestPath)); 1540 Manifest generatedManifest = YamlParser::CreateFromPath(generatedManifestPath); 1541 REQUIRE(generatedManifest.Installers[0].BaseInstallerType == InstallerTypeEnum::MSStore); 1542 REQUIRE(!generatedManifest.Installers[0].ProductId.empty()); 1543 } 1544 1545 YamlManifestInfo CreateYamlManifestInfo(std::string testDataFile) 1546 { 1547 YamlManifestInfo result; 1548 result.Root = AppInstaller::YAML::Load(TestDataFile(testDataFile)); 1549 result.FileName = testDataFile; 1550 return result; 1551 } 1552 1553 TEST_CASE("MultifileManifestInputValidation", "[ManifestValidation]") 1554 { 1555 auto previewManifest = CreateYamlManifestInfo("Manifest-Good.yaml"); 1556 auto v1SingletonManifest = CreateYamlManifestInfo("ManifestV1-Singleton.yaml"); 1557 auto v1VersionManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Version.yaml"); 1558 auto v1InstallerManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Installer.yaml"); 1559 auto v1DefaultLocaleManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-DefaultLocale.yaml"); 1560 auto v1LocaleManifest = CreateYamlManifestInfo("ManifestV1-MultiFile-Locale.yaml"); 1561 1562 { 1563 // Preview and multi file manifest together 1564 std::vector<YamlManifestInfo> input = { previewManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; 1565 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("Preview manifest does not support multi file manifest format")); 1566 } 1567 1568 { 1569 // Singleton and multi file manifest together 1570 std::vector<YamlManifestInfo> input = { v1SingletonManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; 1571 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should not contain file with the particular ManifestType. [ManifestType] Value: singleton")); 1572 } 1573 1574 { 1575 // More than 1 version manifest 1576 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest }; 1577 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: version")); 1578 } 1579 1580 { 1581 // More than 1 installer manifest 1582 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1InstallerManifest, v1InstallerManifest, v1DefaultLocaleManifest }; 1583 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: installer")); 1584 } 1585 1586 { 1587 // More than 1 default locale manifest 1588 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest, v1DefaultLocaleManifest }; 1589 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: defaultLocale")); 1590 } 1591 1592 { 1593 // Duplicate locales 1594 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1InstallerManifest, v1DefaultLocaleManifest, v1LocaleManifest, v1LocaleManifest }; 1595 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest contains duplicate PackageLocale. [PackageLocale] Value: en-GB")); 1596 } 1597 1598 { 1599 // default locale not match 1600 auto defaultLocaleManifestCopy = v1DefaultLocaleManifest; 1601 defaultLocaleManifestCopy.Root["PackageLocale"].SetScalar("fr-fr"); 1602 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1InstallerManifest, defaultLocaleManifestCopy, v1LocaleManifest }; 1603 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("DefaultLocale value in version manifest does not match PackageLocale value in defaultLocale manifest")); 1604 } 1605 1606 { 1607 // Package Id does not match 1608 auto installerManifestCopy = v1InstallerManifest; 1609 installerManifestCopy.Root["PackageIdentifier"].SetScalar("Another.Identifier"); 1610 std::vector<YamlManifestInfo> input = { v1VersionManifest, installerManifestCopy, v1DefaultLocaleManifest, v1LocaleManifest }; 1611 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest has inconsistent field values. [PackageIdentifier] Value: Another.Identifier")); 1612 } 1613 1614 { 1615 // Package Version does not match 1616 auto installerManifestCopy = v1InstallerManifest; 1617 installerManifestCopy.Root["PackageVersion"].SetScalar("Another.Version"); 1618 std::vector<YamlManifestInfo> input = { v1VersionManifest, installerManifestCopy, v1DefaultLocaleManifest, v1LocaleManifest }; 1619 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest has inconsistent field values. [PackageVersion] Value: Another.Version")); 1620 } 1621 1622 { 1623 // Incomplete multi file manifest, missing installer 1624 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1DefaultLocaleManifest }; 1625 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest is incomplete")); 1626 } 1627 1628 { 1629 // Incomplete multi file manifest, missing default locale 1630 std::vector<YamlManifestInfo> input = { v1VersionManifest, v1InstallerManifest }; 1631 REQUIRE_THROWS_MATCHES(YamlParser::ParseManifest(input), ManifestException, ManifestExceptionMatcher("The multi file manifest is incomplete")); 1632 } 1633 } 1634 1635 TEST_CASE("ManifestApplyLocale", "[ManifestValidation]") 1636 { 1637 Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); 1638 1639 // No better alternative locale, default is used. 1640 manifest.ApplyLocale("zh-CN"); 1641 REQUIRE(manifest.CurrentLocalization.Locale == "es-MX"); 1642 REQUIRE(manifest.CurrentLocalization.Get<Localization::PackageName>() == "es-MX package name"); 1643 REQUIRE(manifest.CurrentLocalization.Get<Localization::Publisher>() == "es-MX publisher"); 1644 1645 // en-US results in en-GB, which is better than default. 1646 manifest.ApplyLocale("en-US"); 1647 REQUIRE(manifest.CurrentLocalization.Locale == "en-GB"); 1648 REQUIRE(manifest.CurrentLocalization.Get<Localization::PackageName>() == "en-GB package name"); 1649 REQUIRE(manifest.CurrentLocalization.Get<Localization::Publisher>() == "en-GB publisher"); 1650 1651 // fr-FR results in fr-FR, but only package name is localized. 1652 manifest.ApplyLocale("fr-FR"); 1653 REQUIRE(manifest.CurrentLocalization.Locale == "fr-FR"); 1654 REQUIRE(manifest.CurrentLocalization.Get<Localization::PackageName>() == "fr-FR package name"); 1655 REQUIRE(manifest.CurrentLocalization.Get<Localization::Publisher>() == "es-MX publisher"); 1656 } 1657 1658 TEST_CASE("ManifestLocalizationValidation", "[ManifestValidation]") 1659 { 1660 Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultiLocale.yaml")); 1661 1662 // Set 1 locale to bad value 1663 manifest.Localizations.at(0).Locale = "Invalid"; 1664 1665 // Full validation should detect as error 1666 auto errors = ValidateManifest(manifest, true); 1667 REQUIRE(errors.size() == 1); 1668 REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); 1669 1670 // Not full validation should detect as warning 1671 errors = ValidateManifest(manifest, false); 1672 REQUIRE(errors.size() == 1); 1673 REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Warning); 1674 } 1675 1676 TEST_CASE("PortableFileTypeValidation", "[ManifestValidation]") 1677 { 1678 Manifest installerManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-InstallerTypeZip-PortableNotExe.yaml")); 1679 Manifest rootManifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-InstallerTypeZip-PortableNotExe_Root.yaml")); 1680 1681 // Regular validation should detect as error 1682 auto errors = ValidateManifest(installerManifest, true); 1683 REQUIRE(errors.size() == 1); 1684 REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); 1685 1686 errors = ValidateManifest(rootManifest, true); 1687 REQUIRE(errors.size() == 1); 1688 REQUIRE(errors.at(0).ErrorLevel == ValidationError::Level::Error); 1689 1690 // Should not error when full validation is set to false 1691 errors = ValidateManifest(installerManifest, false); 1692 REQUIRE(errors.size() == 0); 1693 1694 errors = ValidateManifest(rootManifest, false); 1695 REQUIRE(errors.size() == 0); 1696 } 1697 1698 TEST_CASE("ReadManifestAndValidateMsixInstallers_Success", "[ManifestValidation]") 1699 { 1700 TestDataFile testFile("Manifest-Good-MsixInstaller.yaml"); 1701 Manifest manifest = YamlParser::CreateFromPath(testFile); 1702 1703 // Update the installer path for testing 1704 REQUIRE(1 == manifest.Installers.size()); 1705 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1706 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1707 1708 auto errors = ValidateManifestInstallers(manifest); 1709 REQUIRE(0 == errors.size()); 1710 } 1711 1712 TEST_CASE("ReadManifestAndValidateMsixInstallers_InconsistentFields", "[ManifestValidation]") 1713 { 1714 TestDataFile testFile("Manifest-Bad-InconsistentMsixInstallerFields.yaml"); 1715 Manifest manifest = YamlParser::CreateFromPath(testFile); 1716 1717 // Update the installer path for testing 1718 REQUIRE(1 == manifest.Installers.size()); 1719 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1720 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1721 1722 auto errors = ValidateManifestInstallers(manifest); 1723 REQUIRE(4 == errors.size()); 1724 1725 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::MsixSignatureHashFailed); 1726 ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1727 ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1728 ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.0.0"); 1729 } 1730 1731 TEST_CASE("ReadManifestAndValidateMsixInstallers_NoSupportedPlatforms", "[ManifestValidation]") 1732 { 1733 auto testFileName = "Manifest-Bad-NoSupportedPlatforms.yaml"; 1734 TestDataFile testFile(testFileName); 1735 Manifest manifest = YamlParser::CreateFromPath(testFile); 1736 1737 // Update the installer path for testing 1738 REQUIRE(1 == manifest.Installers.size()); 1739 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1740 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1741 1742 auto errors = ValidateManifestInstallers(manifest); 1743 REQUIRE(1 == errors.size()); 1744 1745 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::NoSupportedPlatforms, "InstallerUrl", manifest.Installers.front().Url); 1746 } 1747 1748 TEST_CASE("ReadManifestAndValidateMsixInstallers_PackageVersionNotUINT64", "[ManifestValidation]") 1749 { 1750 Manifest manifest = YamlParser::CreateFromPath(TestDataFile("Manifest-Bad-MsixInstaller-PackageVersion.yaml")); 1751 1752 // Update the installer path for testing 1753 REQUIRE(1 == manifest.Installers.size()); 1754 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1755 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1756 1757 auto errors = ValidateManifestInstallers(manifest); 1758 REQUIRE(1 == errors.size()); 1759 1760 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1761 } 1762 1763 TEST_CASE("ReadManifestAndValidateMsixInstallers_MissingFields", "[ManifestValidation]") 1764 { 1765 TestDataFile testFile("Manifest-Bad-MissingMsixInstallerFields.yaml"); 1766 Manifest manifest = YamlParser::CreateFromPath(testFile); 1767 1768 // Update the installer path for testing 1769 REQUIRE(1 == manifest.Installers.size()); 1770 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1771 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1772 1773 for (bool treatErrorAsWarning : { false, true }) 1774 { 1775 auto errors = ValidateManifestInstallers(manifest, treatErrorAsWarning); 1776 auto expectedLevel = treatErrorAsWarning ? ValidationError::Level::Warning : ValidationError::Level::Error; 1777 REQUIRE(2 == errors.size()); 1778 1779 ValidateError(errors[0], expectedLevel, ManifestError::OptionalFieldMissing, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1780 ValidateError(errors[1], expectedLevel, ManifestError::OptionalFieldMissing, "MinimumOSVersion", "10.0.0.0"); 1781 } 1782 } 1783 1784 TEST_CASE("ReadManifestAndValidateMsixInstallers_Signed_Success", "[ManifestValidation]") 1785 { 1786 TestDataFile testFile("Manifest-Good-SignedMsixInstaller.yaml"); 1787 Manifest manifest = YamlParser::CreateFromPath(testFile); 1788 1789 // Update the installer path for testing 1790 REQUIRE(1 == manifest.Installers.size()); 1791 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1792 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1793 1794 auto errors = ValidateManifestInstallers(manifest); 1795 REQUIRE(0 == errors.size()); 1796 } 1797 1798 TEST_CASE("ReadManifestAndValidateMsixInstallers_Signed_InconsistentFields", "[ManifestValidation]") 1799 { 1800 TestDataFile testFile("Manifest-Bad-InconsistentSignedMsixInstallerFields.yaml"); 1801 Manifest manifest = YamlParser::CreateFromPath(testFile); 1802 1803 // Update the installer path for testing 1804 REQUIRE(1 == manifest.Installers.size()); 1805 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1806 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1807 1808 auto errors = ValidateManifestInstallers(manifest); 1809 REQUIRE(4 == errors.size()); 1810 1811 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "SignatureSha256", "50562001202c8dad456474d3f20903138d0a15c44ee497c3d4f82e85edbf2f97"); 1812 ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1813 ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1814 ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.0.0"); 1815 } 1816 1817 TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Success", "[ManifestValidation]") 1818 { 1819 TestDataFile testFile("Manifest-Good-MsixBundleInstaller.yaml"); 1820 Manifest manifest = YamlParser::CreateFromPath(testFile); 1821 1822 // Update the installer path for testing 1823 REQUIRE(1 == manifest.Installers.size()); 1824 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1825 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1826 1827 auto errors = ValidateManifestInstallers(manifest); 1828 REQUIRE(0 == errors.size()); 1829 } 1830 1831 TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_WithStub_Success", "[ManifestValidation]") 1832 { 1833 TestDataFile testFile("Manifest-Good-MsixBundleInstaller-WithStub.yaml"); 1834 Manifest manifest = YamlParser::CreateFromPath(testFile); 1835 1836 // Update the installer path for testing 1837 REQUIRE(1 == manifest.Installers.size()); 1838 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1839 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1840 1841 auto errors = ValidateManifestInstallers(manifest); 1842 REQUIRE(0 == errors.size()); 1843 } 1844 1845 TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_InconsistentFields", "[ManifestValidation]") 1846 { 1847 TestDataFile testFile("Manifest-Bad-InconsistentMsixBundleInstallerFields.yaml"); 1848 Manifest manifest = YamlParser::CreateFromPath(testFile); 1849 1850 // Update the installer path for testing 1851 REQUIRE(1 == manifest.Installers.size()); 1852 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1853 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1854 1855 auto errors = ValidateManifestInstallers(manifest); 1856 REQUIRE(7 == errors.size()); 1857 1858 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::MsixSignatureHashFailed); 1859 1860 // Validate errors for the first msix package in the msix bundle 1861 ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1862 ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1863 ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); 1864 1865 // Validate errors for the second msix package in the msix bundle 1866 ValidateError(errors[4], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1867 ValidateError(errors[5], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1868 ValidateError(errors[6], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); 1869 } 1870 1871 TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Signed_Success", "[ManifestValidation]") 1872 { 1873 TestDataFile testFile("Manifest-Good-SignedMsixBundleInstaller.yaml"); 1874 Manifest manifest = YamlParser::CreateFromPath(testFile); 1875 1876 // Update the installer path for testing 1877 REQUIRE(1 == manifest.Installers.size()); 1878 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1879 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1880 1881 auto errors = ValidateManifestInstallers(manifest); 1882 REQUIRE(0 == errors.size()); 1883 } 1884 1885 TEST_CASE("ReadManifestAndValidateMsixBundleInstallers_Signed_InconsistentFields", "[ManifestValidation]") 1886 { 1887 TestDataFile testFile("Manifest-Bad-InconsistentSignedMsixBundleInstallerFields.yaml"); 1888 Manifest manifest = YamlParser::CreateFromPath(testFile); 1889 1890 // Update the installer path for testing 1891 REQUIRE(1 == manifest.Installers.size()); 1892 TestDataFile msixFile(manifest.Installers[0].Url.c_str()); 1893 manifest.Installers[0].Url = msixFile.GetPath().u8string(); 1894 1895 auto errors = ValidateManifestInstallers(manifest); 1896 REQUIRE(7 == errors.size()); 1897 1898 ValidateError(errors[0], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "SignatureSha256", "d70bd623f87b6ce4ddba4506c6000cf43ef3af4ab1207f5579ec43400de1623f"); 1899 1900 // Validate errors for the first msix package in the msix bundle 1901 ValidateError(errors[1], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1902 ValidateError(errors[2], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1903 ValidateError(errors[3], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); 1904 1905 // Validate errors for the second msix package in the msix bundle 1906 ValidateError(errors[4], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", "FakeInstallerForTesting_125rzkzqaqjwj"); 1907 ValidateError(errors[5], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "PackageVersion", "43690.48059.52428.56797"); 1908 ValidateError(errors[6], ValidationError::Level::Error, ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", "10.0.16299.0"); 1909 } 1910 1911 TEST_CASE("ManifestArpVersionRange", "[ManifestValidation]") 1912 { 1913 Manifest manifestNoArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-NoArpVersionDeclared.yaml")); 1914 REQUIRE(manifestNoArp.GetArpVersionRange().IsEmpty()); 1915 1916 Manifest manifestSingleArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-SingleArpVersionDeclared.yaml")); 1917 auto arpRangeSingleArp = manifestSingleArp.GetArpVersionRange(); 1918 REQUIRE(arpRangeSingleArp.GetMinVersion().ToString() == "11.0"); 1919 REQUIRE(arpRangeSingleArp.GetMaxVersion().ToString() == "11.0"); 1920 1921 Manifest manifestMultiArp = YamlParser::CreateFromPath(TestDataFile("Manifest-Good-MultipleArpVersionDeclared.yaml")); 1922 auto arpRangeMultiArp = manifestMultiArp.GetArpVersionRange(); 1923 REQUIRE(arpRangeMultiArp.GetMinVersion().ToString() == "12.0"); 1924 REQUIRE(arpRangeMultiArp.GetMaxVersion().ToString() == "13.0"); 1925 } 1926 1927 TEST_CASE("ManifestV1_10_SchemaHeaderValidations", "[ManifestValidation]") 1928 { 1929 ManifestValidateOption validateOption; 1930 validateOption.FullValidation = true; 1931 1932 // Schema header not found 1933 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderNotFound.yaml"),validateOption), ManifestException, ManifestExceptionMatcher("Schema header not found")); 1934 1935 // Schema header not valid 1936 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderInvalid.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The schema header is invalid. Please verify that the schema header is present and formatted correctly.")); 1937 1938 // Schema header URL does not match the expected schema URL 1939 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderURLPatternMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The schema header URL does not match the expected pattern.")); 1940 1941 // Schema header ManifestType does not match the expected value 1942 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderManifestTypeMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The manifest type in the schema header does not match the ManifestType property value in the manifest.")); 1943 1944 // Schema header version does not match the expected version 1945 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(TestDataFile("ManifestV1_10-Bad-SchemaHeaderManifestVersionMismatch.yaml"), validateOption), ManifestException, ManifestExceptionMatcher("The manifest version in the schema header does not match the ManifestVersion property value in the manifest.")); 1946 } 1947 1948 TEST_CASE("ShadowManifest", "[ShadowManifest]") 1949 { 1950 ManifestValidateOption validateOption; 1951 validateOption.FullValidation = true; 1952 validateOption.AllowShadowManifest = true; 1953 1954 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1955 CopyTestDataFilesToFolder({ 1956 "ManifestV1_5-MultiFile-Version.yaml", 1957 "ManifestV1_5-Shadow-Installer.yaml", 1958 "ManifestV1_5-Shadow-DefaultLocale.yaml", 1959 "ManifestV1_5-Shadow-Locale.yaml", 1960 "ManifestV1_5-Shadow-Locale2.yaml", 1961 "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); 1962 1963 auto shadowInfo = ManifestShadowTestInfo{}; 1964 shadowInfo.shadowDefaultLocale = true; 1965 shadowInfo.shadowEnGbLocale = true; 1966 1967 TempFile mergedManifestFile{ "merged.yaml" }; 1968 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1969 VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); 1970 1971 // Read from merged manifest should have the same content as multi file manifest 1972 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 1973 VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); 1974 } 1975 1976 TEST_CASE("ShadowManifest_SkipShadowDefaultLocale", "[ShadowManifest]") 1977 { 1978 ManifestValidateOption validateOption; 1979 validateOption.FullValidation = true; 1980 validateOption.AllowShadowManifest = true; 1981 1982 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 1983 CopyTestDataFilesToFolder({ 1984 "ManifestV1_5-MultiFile-Version.yaml", 1985 "ManifestV1_5-Shadow-Installer.yaml", 1986 "ManifestV1_5-MultiFile-DefaultLocale.yaml", 1987 "ManifestV1_5-Shadow-Locale.yaml", 1988 "ManifestV1_5-Shadow-Locale2.yaml", 1989 "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); 1990 1991 auto shadowInfo = ManifestShadowTestInfo{}; 1992 shadowInfo.shadowDefaultLocale = false; 1993 shadowInfo.shadowEnGbLocale = true; 1994 1995 TempFile mergedManifestFile{ "merged.yaml" }; 1996 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 1997 VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); 1998 1999 // Read from merged manifest should have the same content as multi file manifest 2000 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 2001 VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); 2002 } 2003 2004 TEST_CASE("ShadowManifest_SkipShadowLocalizationLocale", "[ShadowManifest]") 2005 { 2006 ManifestValidateOption validateOption; 2007 validateOption.FullValidation = true; 2008 validateOption.AllowShadowManifest = true; 2009 2010 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 2011 CopyTestDataFilesToFolder({ 2012 "ManifestV1_5-MultiFile-Version.yaml", 2013 "ManifestV1_5-Shadow-Installer.yaml", 2014 "ManifestV1_5-Shadow-DefaultLocale.yaml", 2015 "ManifestV1_5-MultiFile-Locale.yaml", 2016 "ManifestV1_5-Shadow-Locale2.yaml", 2017 "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); 2018 2019 auto shadowInfo = ManifestShadowTestInfo{}; 2020 shadowInfo.shadowDefaultLocale = true; 2021 shadowInfo.shadowEnGbLocale = false; 2022 2023 TempFile mergedManifestFile{ "merged.yaml" }; 2024 Manifest multiFileManifest = YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile); 2025 VerifyV1ManifestContentCreatedWithShadow(multiFileManifest, shadowInfo); 2026 2027 // Read from merged manifest should have the same content as multi file manifest 2028 Manifest mergedManifest = YamlParser::CreateFromPath(mergedManifestFile); 2029 VerifyV1ManifestContentCreatedWithShadow(mergedManifest, shadowInfo); 2030 } 2031 2032 TEST_CASE("ShadowManifest_ShadowNotAllowed", "[ShadowManifest]") 2033 { 2034 ManifestValidateOption validateOption; 2035 validateOption.FullValidation = true; 2036 validateOption.AllowShadowManifest = false; 2037 2038 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 2039 CopyTestDataFilesToFolder({ 2040 "ManifestV1_5-MultiFile-Version.yaml", 2041 "ManifestV1_5-Shadow-Installer.yaml", 2042 "ManifestV1_5-Shadow-DefaultLocale.yaml", 2043 "ManifestV1_5-Shadow-Locale.yaml", 2044 "ManifestV1_5-Shadow-Locale2.yaml", 2045 "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); 2046 2047 TempFile mergedManifestFile{ "merged.yaml" }; 2048 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("Shadow manifest is not allowed. [ManifestType] Value: shadow File: ManifestV1_5-Shadow-Shadow.yaml")); 2049 } 2050 2051 TEST_CASE("ShadowManifest_TwoShadowFiles", "[ShadowManifest]") 2052 { 2053 ManifestValidateOption validateOption; 2054 validateOption.FullValidation = true; 2055 validateOption.AllowShadowManifest = true; 2056 2057 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 2058 CopyTestDataFilesToFolder({ 2059 "ManifestV1_5-MultiFile-Version.yaml", 2060 "ManifestV1_5-Shadow-Installer.yaml", 2061 "ManifestV1_5-Shadow-DefaultLocale.yaml", 2062 "ManifestV1_5-Shadow-Shadow.yaml", 2063 "ManifestV1_5-Shadow-Shadow2.yaml" }, multiFileDirectory); 2064 2065 TempFile mergedManifestFile{ "merged.yaml" }; 2066 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("The multi file manifest should contain only one file with the particular ManifestType. [ManifestType] Value: shadow File: ManifestV1_5-Shadow-Shadow2.yaml")); 2067 } 2068 2069 TEST_CASE("ShadowManifest_NotVerifiedPublisher", "[ShadowManifest]") 2070 { 2071 ManifestValidateOption validateOption; 2072 validateOption.FullValidation = true; 2073 validateOption.AllowShadowManifest = true; 2074 validateOption.ErrorOnVerifiedPublisherFields = true; 2075 2076 TempDirectory multiFileDirectory{ "MultiFileManifest" }; 2077 CopyTestDataFilesToFolder({ 2078 "ManifestV1_5-MultiFile-Version.yaml", 2079 "ManifestV1_5-Shadow-Installer.yaml", 2080 "ManifestV1_5-Shadow-DefaultLocale.yaml", 2081 "ManifestV1_5-Shadow-Locale.yaml", 2082 "ManifestV1_5-Shadow-Locale2.yaml", 2083 "ManifestV1_5-Shadow-Shadow.yaml" }, multiFileDirectory); 2084 2085 TempFile mergedManifestFile{ "merged.yaml" }; 2086 REQUIRE_THROWS_MATCHES(YamlParser::CreateFromPath(multiFileDirectory, validateOption, mergedManifestFile), ManifestException, ManifestExceptionMatcher("Field usage requires verified publishers. [Icons]")); 2087 }