commit 0d30eb38e2472510b8a67429dd1db2607032819c parent 0699598f8d1ee7962284c52e4ab9b0ac4a5f1217 Author: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Tue, 24 Mar 2020 11:51:19 -0700 Add more validations to manifest creation (#66) Diffstat:
77 files changed, 1344 insertions(+), 264 deletions(-)
diff --git a/src/AppInstallerCLI/AppInstallerCLI.vcxproj b/src/AppInstallerCLI/AppInstallerCLI.vcxproj @@ -134,7 +134,7 @@ <Link> <SubSystem>Console</SubSystem> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)'=='Debug'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)'=='Debug'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> @@ -164,7 +164,7 @@ <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)'=='Release'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)'=='Release'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -115,7 +115,7 @@ <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <WarningLevel>Level4</WarningLevel> - <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions> + <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING</AdditionalOptions> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -101,7 +101,7 @@ <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> <PreprocessorDefinitions>_CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> <WarningLevel>Level4</WarningLevel> - <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions> + <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING</AdditionalOptions> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> @@ -114,7 +114,7 @@ <Link> <SubSystem>Console</SubSystem> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> @@ -124,7 +124,7 @@ <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError> </ClCompile> <Link> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> @@ -143,8 +143,8 @@ <EnableCOMDATFolding>true</EnableCOMDATFolding> <OptimizeReferences>true</OptimizeReferences> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> @@ -180,36 +180,33 @@ <ClCompile Include="YamlManifest.cpp" /> </ItemGroup> <ItemGroup> - <CopyFileToFolders Include="TestData\GoodManifest.yml"> + <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> + <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yml"> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yml"> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yml"> - <DeploymentContent>true</DeploymentContent> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yml"> + <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> <None Include="packages.config" /> <None Include="PropertySheet.props" /> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yml"> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yml"> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yml"> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yml"> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> @@ -222,6 +219,132 @@ <DeploymentContent>true</DeploymentContent> </CopyFileToFolders> <None Include="Run-TestsInPackage.ps1" /> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Installeruniqueness-DefaultLang.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Installeruniqueness-DiffLangs.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> </ItemGroup> <ItemGroup> <ProjectReference Include="..\AppInstallerCLICore\AppInstallerCLICore.vcxproj"> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -90,34 +90,130 @@ <None Include="Run-TestsInPackage.ps1" /> </ItemGroup> <ItemGroup> - <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchInvalid.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\GoodManifest.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-ArchMissing.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-Channel-NotSupported.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-camelCase.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-lower.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DifferentCase-UPPER.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> - <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yml"> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-IdMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallersMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerTypeMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-DefaultValues.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-InstallerUniqueness-SameLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-NameMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-PublisherMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Invalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-Sha256Missing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-SwitchInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UnknownProperty.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-UrlMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionInvalid.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Bad-VersionMissing.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-Silent.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerTypeExe-SilentRoot.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Installeruniqueness-DefaultLang.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Installeruniqueness-DiffLangs.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-InstallerUniqueness-DiffScope.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Minimum-InstallerType.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good-Switches.yaml"> <Filter>TestData</Filter> </CopyFileToFolders> <CopyFileToFolders Include="TestData\index.1.0.0.0.msix"> @@ -129,5 +225,32 @@ <CopyFileToFolders Include="TestData\TestSignedApp.msix"> <Filter>TestData</Filter> </CopyFileToFolders> + <CopyFileToFolders Include="TestData\Manifest-Good.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_NoApplicableArchitecture.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_StreamingFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Msix_DownloadFlow.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallFlowTest_Exe.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Msi_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_WithSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\InstallerArgTest_Inno_NoSwitches.yaml"> + <Filter>TestData</Filter> + </CopyFileToFolders> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/AppInstallerCLITests/SQLiteIndex.cpp b/src/AppInstallerCLITests/SQLiteIndex.cpp @@ -37,7 +37,7 @@ SQLiteIndex SimpleTestSetup(const std::string& filePath, Manifest& manifest, std manifest.Tags = { "t1", "t2" }; manifest.Commands = { "test1", "test2" }; - relativePath = "test/id/1.0.0.yml"; + relativePath = "test/id/1.0.0.yaml"; index.AddManifest(manifest, relativePath); @@ -138,8 +138,8 @@ TEST_CASE("SQLiteIndexCreateAndAddManifestFile", "[sqliteindex]") SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, Schema::Version::Latest()); - TestDataFile manifestFile{ "GoodManifest.yml" }; - std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yml" }; + TestDataFile manifestFile{ "Manifest-Good.yaml" }; + std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); } @@ -155,7 +155,7 @@ TEST_CASE("SQLiteIndexCreateAndAddManifestDuplicate", "[sqliteindex]") SQLiteIndex index = SimpleTestSetup(tempFile, manifest, relativePath); // Attempting to add the same manifest at a different path should fail. - REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); + REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yaml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); // Attempting to add a different manifest at the same path should fail. manifest.Id += "-new"; @@ -166,8 +166,8 @@ TEST_CASE("SQLiteIndex_RemoveManifestFile_NotPresent", "[sqliteindex]") { SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, Schema::Version::Latest()); - TestDataFile manifestFile{ "GoodManifest.yml" }; - std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yml" }; + TestDataFile manifestFile{ "Manifest-Good.yaml" }; + std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; REQUIRE_THROWS_HR(index.RemoveManifest(manifestFile, manifestPath), E_NOT_SET); } @@ -177,7 +177,7 @@ TEST_CASE("SQLiteIndex_RemoveManifest", "[sqliteindex][V1_0]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - std::string manifest1Path = "test/id/test.id-1.0.0.yml"; + std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; Manifest manifest1; manifest1.Id = "test.id"; manifest1.Name = "Test Name"; @@ -187,7 +187,7 @@ TEST_CASE("SQLiteIndex_RemoveManifest", "[sqliteindex][V1_0]") manifest1.Tags = { "t1", "t2" }; manifest1.Commands = { "test1", "test2" }; - std::string manifest2Path = "test/woah/test.id-1.0.0.yml"; + std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; Manifest manifest2; manifest2.Id = "test.woah"; manifest2.Name = "Test Name WOAH"; @@ -252,8 +252,8 @@ TEST_CASE("SQLiteIndex_RemoveManifestFile", "[sqliteindex][V1_0]") { SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); - TestDataFile manifestFile{ "GoodManifest.yml" }; - std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yml" }; + TestDataFile manifestFile{ "Manifest-Good.yaml" }; + std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); @@ -280,7 +280,7 @@ TEST_CASE("SQLiteIndex_UpdateManifest", "[sqliteindex][V1_0]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - std::string manifestPath = "test/id/test.id-1.0.0.yml"; + std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Id = "test.id"; manifest.Name = "Test Name"; @@ -373,7 +373,7 @@ TEST_CASE("SQLiteIndex_UpdateManifestChangePath", "[sqliteindex][V1_0]") TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; INFO("Using temporary file named: " << tempFile.GetPath()); - std::string manifestPath = "test/id/test.id-1.0.0.yml"; + std::string manifestPath = "test/id/test.id-1.0.0.yaml"; Manifest manifest; manifest.Id = "test.id"; manifest.Name = "Test Name"; @@ -407,7 +407,7 @@ TEST_CASE("SQLiteIndex_UpdateManifestChangePath", "[sqliteindex][V1_0]") { SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::ReadWrite); - manifestPath = "test/newid/test.newid-1.0.0.yml"; + manifestPath = "test/newid/test.newid-1.0.0.yaml"; // Update with path update should indicate change REQUIRE(index.UpdateManifest(manifest, manifestPath)); @@ -523,8 +523,8 @@ TEST_CASE("SQLiteIndex_PrepareForPackaging", "[sqliteindex]") SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, Schema::Version::Latest()); - TestDataFile manifestFile{ "GoodManifest.yml" }; - std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yml" }; + TestDataFile manifestFile{ "Manifest-Good.yaml" }; + std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; index.AddManifest(manifestFile, manifestPath); diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -16,7 +16,7 @@ std::shared_ptr<SQLiteIndexSource> SimpleTestSetup(const std::string& filePath, { SQLiteIndex index = SQLiteIndex::CreateNew(filePath, Schema::Version::Latest()); - TestDataFile testManifest("GoodManifest.yml"); + TestDataFile testManifest("Manifest-Good.yaml"); manifest = Manifest::CreateFromPath(testManifest); relativePath = testManifest.GetPath().filename().u8string(); diff --git a/src/AppInstallerCLITests/TestData/BadManifest-MissingName.yml b/src/AppInstallerCLITests/TestData/BadManifest-MissingName.yml @@ -1,44 +0,0 @@ -Id: microsoft.msixsdk -ShortId: msixsdk -Version: 1.7.32 -CompanyName: Microsoft -Channel: release -Author: Microsoft -License: MIT License -LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE -MinOSVersion: 0.0.0.0 -Description: The MSIX SDK project is an effort to enable developers -Homepage: https://github.com/microsoft/msix-packaging -Tags: "msix,appx" -Commands: "makemsix,makeappx" -Protocols: "protocol1,protocol2" -FileExtensions: "appx,appxbundle,msix,msixbundle" -# InstallerType and Switches CAN have a "default" value -# on the root. An installer can override them. -InstallerType: Zip -Switches: - Verbose: /verbose - Default: /default - Silent: /silence -Installers: - - Arch: x86 - Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx86.zip - Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF8C82 - Language: en-US - InstallerType: Zip - Scope: user - Switches: - Verbose: /v - Default: /d - Silent: /s - - Arch: x64 - Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdkx64.zip - Sha256: 69D84CA8899800A5575CE31798293CD4FEBAB1D734A07C2E51E56A28E0DF0000 - Language: en-US - InstallerType: Zip - Scope: user -Localization: - - Language: es-MX - Description: El proyecto MSIX SDK es habilita desarrolladores de diferentes - Homepage: https://github.com/microsoft/msix-packaging/es-MX - LicenseUrl: https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX- \ No newline at end of file diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Exe.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_DownloadFlow.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yml b/src/AppInstallerCLITests/TestData/InstallFlowTest_Msix_StreamingFlow.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yml b/src/AppInstallerCLITests/TestData/InstallFlowTest_NoApplicableArchitecture.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_NoSwitches.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Inno_WithSwitches.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_NoSwitches.yaml diff --git a/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yml b/src/AppInstallerCLITests/TestData/InstallerArgTest_Msi_WithSwitches.yaml diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchInvalid.yaml @@ -0,0 +1,10 @@ +# Bad manifest. Invalid Arch value +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: NotAnArch + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-ArchMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No Name +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Channel-NotSupported.yaml @@ -0,0 +1,11 @@ +# Minimum required +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Channel: release +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-UPPER.yaml @@ -0,0 +1,10 @@ +# Bad manifest. InstallerType is UPPER +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +INSTALLERTYPE: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-camelCase.yaml @@ -0,0 +1,10 @@ +# Bad manifest. InstallerType is camelCased +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +installerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DifferentCase-lower.yaml @@ -0,0 +1,10 @@ +# Bad manifest. InstallerType is lower case +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +installertype: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Duplicated key with different casing convention. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +installertype: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey-DifferentCase.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Duplicated key with different casing convention. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +installerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-DuplicateKey.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Duplicated key Id +Id: microsoft.msixsdk +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-IdInvalid.yaml @@ -0,0 +1,10 @@ +# Bad manifest. Invalid Id +Id: m i c rosoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +InstallerType: Zip +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-IdMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No Id +Name: MSIX SDK +Version: 1.7.32 +InstallerType: Zip +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilent.yaml @@ -0,0 +1,12 @@ +# Bad manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + InstallerType: Exe + Switches: + Interactive: /i diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml @@ -0,0 +1,12 @@ +# Bad manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Switches: + Interactive: /i +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + InstallerType: Exe diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml @@ -0,0 +1,12 @@ +# Bad manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Exe +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Switches: + Interactive: /i diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml @@ -0,0 +1,12 @@ +# Bad manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Exe +Switches: + Interactive: /i +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeInvalid.yaml @@ -0,0 +1,10 @@ +# Bad Manifest. Invalid value for InstallerType. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: NotAType +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerTypeMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No InstallerType +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultScope.yaml @@ -0,0 +1,14 @@ +# Bad Manifest. Uniqueness installers same arch, same scope. Default scope is user +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Scope: user diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-DefaultValues.yaml @@ -0,0 +1,13 @@ +# Bad Manifest. Uniqueness installers same arch, same default values +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness-SameLang.yaml @@ -0,0 +1,15 @@ +# Bad Manifest. Uniqueness installers same arch, language and scope +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-US + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-US diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallerUniqueness.yaml @@ -0,0 +1,17 @@ +# Bad Manifest. Uniqueness installers same arch, language and scope +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-US + Scope: user + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-US + Scope: user diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-InstallersMissing.yaml @@ -0,0 +1,6 @@ +# Bad manifest. Installers missing +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +InstallerType: Zip +Publisher: Microsoft diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-NameMissing.yaml @@ -0,0 +1,9 @@ +# Bad maniest. No Name +Id: microsoft.msixsdk +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-PublisherMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. Publisher missing +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Invalid.yaml @@ -0,0 +1,10 @@ +# Bad manifest. Invalid Sha256 +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B6 diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-Sha256Missing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No Sha256 +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-SwitchInvalid.yaml @@ -0,0 +1,12 @@ +# Bad manifest. Incorrect switch key. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Switches: + NotASwitch: /fakeswitch +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UnknownProperty.yaml @@ -0,0 +1,11 @@ +# Bad manifest. Contains a key that is not in the manifest object. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD +Fake: this is Fake diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlInvalid.yaml @@ -0,0 +1,10 @@ +# Bad maniest. Invalid Url +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: NotAUrl + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-UrlMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No Url +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionInvalid.yaml @@ -0,0 +1,10 @@ +# Bad manifest. Invalid version +Id: microsoft.msixsdk +Name: MSIX SDK +Version: Not.A.Version +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml b/src/AppInstallerCLITests/TestData/Manifest-Bad-VersionMissing.yaml @@ -0,0 +1,9 @@ +# Bad manifest. No Version +Id: microsoft.msixsdk +Name: MSIX SDK +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-Silent.yaml @@ -0,0 +1,13 @@ +# Good manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + InstallerType: Exe + Switches: + Silent: /s + SilentWithProgress: /s diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExe-SilentRoot.yaml @@ -0,0 +1,13 @@ +# Good manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Switches: + Silent: /s + SilentWithProgress: /s +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + InstallerType: Exe diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-Silent.yaml @@ -0,0 +1,13 @@ +# Good manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Exe +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Switches: + Silent: /s + SilentWithProgress: /s diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml @@ -0,0 +1,13 @@ +# Good manifest. Installer Type exe requires Silent switch. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Exe +Switches: + Silent: /s + SilentWithProgress: /s +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-InstallerUniqueness-DiffScope.yaml @@ -0,0 +1,15 @@ +# Good Manifest. Uniqueness installers different scope same arch +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Scope: user + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Scope: system diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DefaultLang.yaml @@ -0,0 +1,14 @@ +# Good Manifest. Uniqueness installers different languages same arch, with default language +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-MX diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Installeruniqueness-DiffLangs.yaml @@ -0,0 +1,15 @@ +# Good Manifest. Uniqueness installers different languages same arch +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: en-US + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Language: es-MX diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum-InstallerType.yaml @@ -0,0 +1,10 @@ +# Minimum required IntallerType in Installers +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + InstallerType: Zip diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Minimum.yaml @@ -0,0 +1,10 @@ +# Minimum required +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD diff --git a/src/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml b/src/AppInstallerCLITests/TestData/Manifest-Good-Switches.yaml @@ -0,0 +1,14 @@ +# Good manifest with Switches. +Id: microsoft.msixsdk +Name: MSIX SDK +Version: 1.7.32 +Publisher: Microsoft +InstallerType: Zip +Switches: + SilentWithProgress: /sp +Installers: + - Arch: x86 + Url: https://rubengustorage.blob.core.windows.net/publiccontainer/msixsdk-x86.zip + Sha256: 98B67758CEAFFCBB3FE47838FD0A8D7BD581C2650842D6B2B0E0D49A23270CCD + Switches: + Silent: /s diff --git a/src/AppInstallerCLITests/TestData/GoodManifest.yml b/src/AppInstallerCLITests/TestData/Manifest-Good.yaml diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -102,7 +102,7 @@ struct TestSource : public ISource { if (request.Query.value().Value == "TestQueryReturnOne") { - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( std::make_unique<TestApplication>(manifest), @@ -110,13 +110,13 @@ struct TestSource : public ISource } else if (request.Query.value().Value == "TestQueryReturnTwo") { - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); result.Matches.emplace_back( ResultMatch( std::make_unique<TestApplication>(manifest), ApplicationMatchFilter(ApplicationMatchField::Id, MatchType::Exact, "TestQueryReturnTwo"))); - auto manifest2 = Manifest::CreateFromPath(TestDataFile("GoodManifest.yml")); + auto manifest2 = Manifest::CreateFromPath(TestDataFile("Manifest-Good.yaml")); result.Matches.emplace_back( ResultMatch( std::make_unique<TestApplication>(manifest2), @@ -173,7 +173,7 @@ TEST_CASE("ExeInstallFlowWithTestManifest", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Exe.yaml").GetPath().u8string()); InstallFlowTest testFlow(context); testFlow.Execute(); INFO(installOutput.str()); @@ -194,7 +194,7 @@ TEST_CASE("InstallFlowWithNonApplicableArchitecture", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NoApplicableArchitecture.yml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_NoApplicableArchitecture.yaml").GetPath().u8string()); InstallFlowTest testFlow(context); REQUIRE_THROWS_WITH(testFlow.Execute(), Catch::Contains("No installer with applicable architecture found.")); INFO(installOutput.str()); @@ -210,7 +210,7 @@ TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Todo: point to files from our repo when the repo goes public - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_DownloadFlow.yml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_DownloadFlow.yaml").GetPath().u8string()); InstallFlowTest testFlow(context); testFlow.Execute(); INFO(installOutput.str()); @@ -231,7 +231,7 @@ TEST_CASE("MsixInstallFlow_StreamingFlow", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Todo: point to files from our repo when the repo goes public - context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_StreamingFlow.yml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Msix_StreamingFlow.yaml").GetPath().u8string()); InstallFlowTest testFlow(context); testFlow.Execute(); INFO(installOutput.str()); @@ -251,7 +251,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Default Msi type with no args passed in, no switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); ShellExecuteInstallerHandlerTest testhandler(manifest.Installers.at(0), context); std::string installerArgs = testhandler.TestInstallerArgs(); REQUIRE(installerArgs.find("/passive") != std::string::npos); @@ -262,7 +262,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Msi type with /silent and /log and /custom and /installlocation, no switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"); @@ -277,7 +277,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Msi type with /silent and /log and /custom and /installlocation, switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_WithSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Msi_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"); @@ -293,7 +293,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Default Inno type with no args passed in, no switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); ShellExecuteInstallerHandlerTest testhandler(manifest.Installers.at(0), context); std::string installerArgs = testhandler.TestInstallerArgs(); REQUIRE(installerArgs.find("/SILENT") != std::string::npos); @@ -304,7 +304,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Inno type with /silent and /log and /custom and /installlocation, no switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_NoSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"); @@ -319,7 +319,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Inno type with /silent and /log and /custom and /installlocation, switches specified in manifest - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"); @@ -335,7 +335,7 @@ TEST_CASE("ShellExecuteHandlerInstallerArgs", "[InstallFlow]") std::ostringstream installOutput; Execution::Context context{ installOutput, std::cin }; // Override switch specified. The whole arg passed to installer is overrided. - auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yml")); + auto manifest = Manifest::CreateFromPath(TestDataFile("InstallerArgTest_Inno_WithSwitches.yaml")); context.Args.AddArg(Execution::Args::Type::Silent); context.Args.AddArg(Execution::Args::Type::Log, "MyLog.log"); context.Args.AddArg(Execution::Args::Type::InstallLocation, "MyDir"); diff --git a/src/AppInstallerCLITests/YamlManifest.cpp b/src/AppInstallerCLITests/YamlManifest.cpp @@ -28,9 +28,9 @@ bool operator==(const MultiValue& a, const MultiValue& b) return true; } -TEST_CASE("ReadGoodManifestAndVerifyContents", "[PackageManifestHelper]") +TEST_CASE("ReadGoodManifestAndVerifyContents", "[ManifestValidation]") { - Manifest manifest = Manifest::CreateFromPath(TestDataFile("GoodManifest.yml")); + Manifest manifest = Manifest::CreateFromPath(TestDataFile("Manifest-Good.yaml")); REQUIRE(manifest.Id == "microsoft.msixsdk"); REQUIRE(manifest.Name == "MSIX SDK"); @@ -106,7 +106,80 @@ TEST_CASE("ReadGoodManifestAndVerifyContents", "[PackageManifestHelper]") REQUIRE(localization1.LicenseUrl == "https://github.com/microsoft/msix-packaging/blob/master/LICENSE-es-MX"); } -TEST_CASE("ReadBadManifestAndVerifyThrow", "[PackageManifestHelper]") +void TestManifest(const std::filesystem::path& manifestPath, const std::string& expectedError = {}) { - REQUIRE_THROWS_WITH(Manifest::CreateFromPath(TestDataFile("BadManifest-MissingName.yml")), Catch::Contains("invalid node; first invalid key: \"Name\"")); + if (expectedError.empty()) + { + CHECK_NOTHROW(Manifest::CreateFromPath(TestDataFile(manifestPath), true)); + } + else + { + CHECK_THROWS_WITH(Manifest::CreateFromPath(TestDataFile(manifestPath), true), Catch::Contains(expectedError)); + } +} + +TEST_CASE("ReadGoodManifests", "[ManifestValidation]") +{ + std::string TestCases[] = + { + "Manifest-Good-InstallerTypeExeRoot-Silent.yaml", + "Manifest-Good-InstallerTypeExeRoot-SilentRoot.yaml", + "Manifest-Good-InstallerTypeExe-Silent.yaml", + "Manifest-Good-InstallerTypeExe-SilentRoot.yaml", + "Manifest-Good-Installeruniqueness-DefaultLang.yaml", + "Manifest-Good-Installeruniqueness-DiffLangs.yaml", + "Manifest-Good-InstallerUniqueness-DiffScope.yaml", + "Manifest-Good-Minimum.yaml", + "Manifest-Good-Minimum-InstallerType.yaml", + "Manifest-Good-Switches.yaml", + }; + + for (auto const& testCase : TestCases) + { + TestManifest(testCase); + } +} + +TEST_CASE("ReadBadManifests", "[ManifestValidation]") +{ + std::pair<std::string, std::string> TestCases[] = + { + { "Manifest-Bad-ArchInvalid.yaml", "Manifest: Invalid field value. Field: Arch" }, + { "Manifest-Bad-ArchMissing.yaml", "Manifest: Required field missing. Field: Arch" }, + { "Manifest-Bad-Channel-NotSupported.yaml", "Manifest: Field is not supported. Field: Channel" }, + { "Manifest-Bad-DifferentCase-camelCase.yaml", "Manifest: All field names should be PascalCased. Field: installerType" }, + { "Manifest-Bad-DifferentCase-lower.yaml", "Manifest: All field names should be PascalCased. Field: installertype" }, + { "Manifest-Bad-DifferentCase-UPPER.yaml", "Manifest: All field names should be PascalCased. Field: INSTALLERTYPE" }, + { "Manifest-Bad-DuplicateKey.yaml", "Manifest: Duplicate field found in the manifest." }, + { "Manifest-Bad-DuplicateKey-DifferentCase.yaml", "Manifest: Duplicate field found in the manifest." }, + { "Manifest-Bad-DuplicateKey-DifferentCase-lower.yaml", "Manifest: Duplicate field found in the manifest." }, + { "Manifest-Bad-IdInvalid.yaml", "Manifest: Invalid field value. Field: Id" }, + { "Manifest-Bad-IdMissing.yaml", "Manifest: Required field missing. Field: Id" }, + { "Manifest-Bad-InstallersMissing.yaml", "Manifest: Required field missing. Field: Installers" }, + { "Manifest-Bad-InstallerTypeExe-NoSilent.yaml", "Manifest: Silent switches are required for InstallerType exe." }, + { "Manifest-Bad-InstallerTypeExe-NoSilentRoot.yaml", "Manifest: Silent switches are required for InstallerType exe." }, + { "Manifest-Bad-InstallerTypeExeRoot-NoSilent.yaml", "Manifest: Silent switches are required for InstallerType exe." }, + { "Manifest-Bad-InstallerTypeExeRoot-NoSilentRoot.yaml", "Manifest: Silent switches are required for InstallerType exe." }, + { "Manifest-Bad-InstallerTypeInvalid.yaml", "Manifest: Invalid field value. Field: InstallerType" }, + { "Manifest-Bad-InstallerTypeMissing.yaml", "Manifest: Invalid field value. Field: InstallerType" }, + { "Manifest-Bad-InstallerUniqueness.yaml", "Manifest: Duplicate installer entry found." }, + { "Manifest-Bad-InstallerUniqueness-DefaultScope.yaml", "Manifest: Duplicate installer entry found." }, + { "Manifest-Bad-InstallerUniqueness-DefaultValues.yaml", "Manifest: Duplicate installer entry found." }, + { "Manifest-Bad-InstallerUniqueness-SameLang.yaml", "Manifest: Duplicate installer entry found." }, + { "Manifest-Bad-NameMissing.yaml", "Manifest: Required field missing. Field: Name" }, + { "Manifest-Bad-PublisherMissing.yaml", "Manifest: Required field missing. Field: Publisher" }, + { "Manifest-Bad-Sha256Invalid.yaml", "Manifest: Invalid field value. Field: Sha256" }, + { "Manifest-Bad-Sha256Missing.yaml", "Manifest: Required field missing. Field: Sha256" }, + { "Manifest-Bad-SwitchInvalid.yaml", "Manifest: Unknown field. Field: NotASwitch" }, + { "Manifest-Bad-UnknownProperty.yaml", "Manifest: Unknown field. Field: Fake" }, + { "Manifest-Bad-UrlInvalid.yaml", "Manifest: Invalid field value. Field: Url" }, + { "Manifest-Bad-UrlMissing.yaml", "Manifest: Required field missing. Field: Url" }, + { "Manifest-Bad-VersionInvalid.yaml", "Manifest: Invalid field value. Field: Version" }, + { "Manifest-Bad-VersionMissing.yaml", "Manifest: Required field missing. Field: Version" }, + }; + + for (auto const& testCase : TestCases) + { + TestManifest(testCase.first, testCase.second); + } } diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -115,7 +115,7 @@ <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <WarningLevel>Level4</WarningLevel> - <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions> + <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING</AdditionalOptions> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> @@ -176,6 +176,7 @@ <ClInclude Include="Manifest\Manifest.h" /> <ClInclude Include="Manifest\ManifestInstaller.h" /> <ClInclude Include="Manifest\ManifestLocalization.h" /> + <ClInclude Include="Manifest\ManifestValidation.h" /> <ClInclude Include="Microsoft\PreIndexedPackageSourceFactory.h" /> <ClInclude Include="Microsoft\Schema\1_0\ChannelTable.h" /> <ClInclude Include="Microsoft\Schema\1_0\CommandsTable.h" /> @@ -217,6 +218,7 @@ <ClCompile Include="Manifest\Manifest.cpp" /> <ClCompile Include="Manifest\ManifestInstaller.cpp" /> <ClCompile Include="Manifest\ManifestLocalization.cpp" /> + <ClCompile Include="Manifest\ManifestValidation.cpp" /> <ClCompile Include="Microsoft\PreIndexedPackageSourceFactory.cpp" /> <ClCompile Include="Microsoft\Schema\1_0\Interface.cpp" /> <ClCompile Include="Microsoft\Schema\1_0\ManifestTable.cpp" /> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -123,6 +123,9 @@ <ClInclude Include="ICU\SQLiteICU.h"> <Filter>ICU</Filter> </ClInclude> + <ClInclude Include="Manifest\ManifestValidation.h"> + <Filter>Manifest</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -185,6 +188,9 @@ <ClCompile Include="ICU\SQLiteICU.c"> <Filter>ICU</Filter> </ClCompile> + <ClCompile Include="Manifest\ManifestValidation.cpp"> + <Filter>Manifest</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp b/src/AppInstallerRepositoryCore/Manifest/Manifest.cpp @@ -36,35 +36,44 @@ namespace AppInstaller::Manifest } } - void Manifest::PopulateManifestFields(const YAML::Node& rootNode) + std::vector<ValidationError> Manifest::PopulateManifestFields(const YAML::Node& rootNode, bool fullValidation) { - // Required fields - this->Id = rootNode["Id"].as<std::string>(); - this->Name = rootNode["Name"].as<std::string>(); - this->Version = rootNode["Version"].as<std::string>(); - this->Publisher = rootNode["Publisher"].as<std::string>(); - - // Optional fields. - this->AppMoniker = rootNode["AppMoniker"] ? rootNode["AppMoniker"].as<std::string>() : ""; - this->Channel = rootNode["Channel"] ? rootNode["Channel"].as<std::string>() : ""; - this->Author = rootNode["Author"] ? rootNode["Author"].as<std::string>() : ""; - this->License = rootNode["License"] ? rootNode["License"].as<std::string>() : ""; - this->MinOSVersion = rootNode["MinOSVersion"] ? rootNode["MinOSVersion"].as<std::string>() : ""; - this->Tags = SplitMultiValueField(rootNode["Tags"] ? rootNode["Tags"].as<std::string>() : ""); - this->Commands = SplitMultiValueField(rootNode["Commands"] ? rootNode["Commands"].as<std::string>() : ""); - this->Protocols = SplitMultiValueField(rootNode["Protocols"] ? rootNode["Protocols"].as<std::string>() : ""); - this->FileExtensions = SplitMultiValueField(rootNode["FileExtensions"] ? rootNode["FileExtensions"].as<std::string>() : ""); - this->InstallerType = rootNode["InstallerType"] ? - ManifestInstaller::ConvertToInstallerTypeEnum(rootNode["InstallerType"].as<std::string>()) : - ManifestInstaller::InstallerTypeEnum::Unknown; - this->Description = rootNode["Description"] ? rootNode["Description"].as<std::string>() : ""; - this->Homepage = rootNode["Homepage"] ? rootNode["Homepage"].as<std::string>() : ""; - this->LicenseUrl = rootNode["LicenseUrl"] ? rootNode["LicenseUrl"].as<std::string>() : ""; - - if (rootNode["Switches"]) + YAML::Node switchesNode; + YAML::Node installersNode; + YAML::Node localizationsNode; + + const std::vector<ManifestFieldInfo> FieldInfos = + { + { "Id", [this](const YAML::Node& value) { Id = value.as<std::string>(); }, true, "^[\\S]+\\.[\\S]+$" }, + { "Name", [this](const YAML::Node& value) { Name = value.as<std::string>(); }, true }, + { "Version", [this](const YAML::Node& value) { Version = value.as<std::string>(); }, true, + "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, + { "Publisher", [this](const YAML::Node& value) { Publisher = value.as<std::string>(); }, true }, + { "AppMoniker", [this](const YAML::Node& value) { AppMoniker = value.as<std::string>(); } }, + { "Channel", [this](const YAML::Node& value) { Channel = value.as<std::string>(); } }, + { "Author", [this](const YAML::Node& value) { Author = value.as<std::string>(); } }, + { "License", [this](const YAML::Node& value) { License = value.as<std::string>(); } }, + { "MinOSVersion", [this](const YAML::Node& value) { MinOSVersion = value.as<std::string>(); }, false, + "^(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(\\.(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])){0,3}$" }, + { "Tags", [this](const YAML::Node& value) { Tags = SplitMultiValueField(value.as<std::string>()); } }, + { "Commands", [this](const YAML::Node& value) { Commands = SplitMultiValueField(value.as<std::string>()); } }, + { "Protocols", [this](const YAML::Node& value) { Protocols = SplitMultiValueField(value.as<std::string>()); } }, + { "FileExtensions", [this](const YAML::Node& value) { FileExtensions = SplitMultiValueField(value.as<std::string>()); } }, + { "InstallerType", [this](const YAML::Node& value) { InstallerType = ManifestInstaller::ConvertToInstallerTypeEnum(value.as<std::string>()); } }, + { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, + { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, + { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, + { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, + { "Installers", [&](const YAML::Node& value) { installersNode = value; }, true }, + { "Localization", [&](const YAML::Node& value) { localizationsNode = value; } }, + }; + + std::vector<ValidationError> resultErrors = ValidateAndProcessFields(rootNode, FieldInfos, fullValidation); + + if (!switchesNode.IsNull()) { - YAML::Node switchesNode = rootNode["Switches"]; - ManifestInstaller::PopulateSwitchesFields(&switchesNode, this->Switches); + auto errors = ManifestInstaller::PopulateSwitchesFields(switchesNode, this->Switches, fullValidation); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } // Create default ManifestInstaller to be used to populate default value when optional fields are not found. @@ -72,11 +81,11 @@ namespace AppInstaller::Manifest defaultInstaller.InstallerType = this->InstallerType; defaultInstaller.Switches = this->Switches; - YAML::Node installersNode = rootNode["Installers"]; for (std::size_t i = 0; i < installersNode.size(); i++) { YAML::Node installerNode = installersNode[i]; ManifestInstaller installer; - installer.PopulateInstallerFields(installerNode, defaultInstaller); + auto errors = installer.PopulateInstallerFields(installerNode, defaultInstaller, fullValidation); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); this->Installers.emplace_back(std::move(installer)); } @@ -86,51 +95,113 @@ namespace AppInstaller::Manifest defaultLocalization.Homepage = this->Homepage; defaultLocalization.LicenseUrl = this->LicenseUrl; - if (rootNode["Localization"]) + if (!localizationsNode.IsNull()) { - YAML::Node localizationsNode = rootNode["Localization"]; for (std::size_t i = 0; i < localizationsNode.size(); i++) { YAML::Node localizationNode = localizationsNode[i]; ManifestLocalization localization; - localization.PopulateLocalizationFields(localizationNode, defaultLocalization); + auto errors = localization.PopulateLocalizationFields(localizationNode, defaultLocalization, fullValidation); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); this->Localization.emplace_back(std::move(localization)); } } + + // Extra semantic validations after basic validation and field population + if (fullValidation) + { + // Channel is not supported currently + if (!Channel.empty()) + { + resultErrors.emplace_back(ManifestError::FieldNotSupported, "Channel", Channel); + } + + // Check duplicate installer entry. {installerType, arch, language and scope} combination is the key. + // Todo: use the comparator from ManifestComparator when that one is fully implemented. + auto installerCmp = [](const ManifestInstaller& in1, const ManifestInstaller& in2) + { + if (in1.InstallerType != in2.InstallerType) + { + return in1.InstallerType < in2.InstallerType; + } + + if (in1.Arch != in2.Arch) + { + return in1.Arch < in2.Arch; + } + + if (in1.Language != in2.Language) + { + return in1.Language < in2.Language; + } + + if (in1.Scope != in2.Scope) + { + return in1.Scope < in2.Scope; + } + + return false; + }; + + std::set<ManifestInstaller, decltype(installerCmp)> installerSet(installerCmp); + + for (auto const& installer : Installers) + { + if (!installerSet.insert(installer).second) + { + resultErrors.emplace_back(ManifestError::DuplicateInstallerEntry); + break; + } + } + } + + return resultErrors; } - Manifest Manifest::CreateFromPath(const std::filesystem::path& inputFile) + Manifest Manifest::CreateFromPath(const std::filesystem::path& inputFile, bool fullValidation) { Manifest manifest; + std::vector<ValidationError> errors; try { YAML::Node rootNode = YAML::LoadFile(inputFile.u8string()); - manifest.PopulateManifestFields(rootNode); + errors = manifest.PopulateManifestFields(rootNode, fullValidation); } - catch (std::runtime_error& e) + catch (const std::exception& e) { AICLI_LOG(YAML, Error, << "Failed to create manifest from file: " << inputFile.u8string()); THROW_EXCEPTION_MSG(ManifestException(), e.what()); } + if (!errors.empty()) + { + THROW_EXCEPTION(ManifestException(std::move(errors))); + } + return manifest; } - Manifest Manifest::Create(const std::string& input) + Manifest Manifest::Create(const std::string& input, bool fullValidation) { Manifest manifest; + std::vector<ValidationError> errors; try { YAML::Node rootNode = YAML::Load(input); - manifest.PopulateManifestFields(rootNode); + errors = manifest.PopulateManifestFields(rootNode, fullValidation); } - catch (std::runtime_error& e) + catch (const std::exception& e) { AICLI_LOG(YAML, Error, << "Failed to create manifest: " << input); THROW_EXCEPTION_MSG(ManifestException(), e.what()); } + if (!errors.empty()) + { + THROW_EXCEPTION(ManifestException(std::move(errors))); + } + return manifest; } } diff --git a/src/AppInstallerRepositoryCore/Manifest/Manifest.h b/src/AppInstallerRepositoryCore/Manifest/Manifest.h @@ -3,24 +3,15 @@ #pragma once #include "ManifestInstaller.h" #include "ManifestLocalization.h" -#include <AppInstallerErrors.h> +#include "ManifestValidation.h" #include <AppInstallerStrings.h> -#include <yaml-cpp/yaml.h> - -#include <wil/result.h> #include <filesystem> #include <string> -#include <optional> #include <vector> namespace AppInstaller::Manifest { - struct ManifestException : public wil::ResultException - { - ManifestException() : wil::ResultException(APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) {} - }; - // Our representation of the parsed manifest file. struct Manifest { @@ -35,10 +26,11 @@ namespace AppInstaller::Manifest // Required string_t Version; - string_t AppMoniker; - + // Required string_t Publisher; + string_t AppMoniker; + string_t Channel; string_t Author; @@ -59,13 +51,7 @@ namespace AppInstaller::Manifest // Comma separated values std::vector<string_t> FileExtensions; - std::vector<ManifestInstaller> Installers; - - std::vector<ManifestLocalization> Localization; - - ManifestInstaller::InstallerTypeEnum InstallerType; - - std::map<ManifestInstaller::InstallerSwitchType, string_t> Switches; + ManifestInstaller::InstallerTypeEnum InstallerType = ManifestInstaller::InstallerTypeEnum::Unknown; string_t Description; @@ -73,10 +59,18 @@ namespace AppInstaller::Manifest string_t LicenseUrl; - void PopulateManifestFields(const YAML::Node& rootNode); + std::map<ManifestInstaller::InstallerSwitchType, string_t> Switches; + + std::vector<ManifestInstaller> Installers; + + std::vector<ManifestLocalization> Localization; + + std::vector<ValidationError> PopulateManifestFields(const YAML::Node& rootNode, bool fullValidation); - static Manifest CreateFromPath(const std::filesystem::path& inputFile); + // fullValidation Bool to set if manifest creation should perform extra validation that client does not need. + // e.g. Channel should be null. Client code does not need this check to work properly. + static Manifest CreateFromPath(const std::filesystem::path& inputFile, bool fullValidation = false); - static Manifest Create(const std::string& input); + static Manifest Create(const std::string& input, bool fullValidation = false); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.cpp @@ -6,74 +6,88 @@ namespace AppInstaller::Manifest { - void ManifestInstaller::PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller) + std::vector<ValidationError> ManifestInstaller::PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller, bool fullValidation) { - // Required fields - this->Arch = Utility::ConvertToArchitectureEnum(installerNode["Arch"].as<std::string>()); - this->Url = installerNode["Url"].as<std::string>(); - this->Sha256 = Utility::SHA256::ConvertToBytes(installerNode["Sha256"].as<std::string>()); + YAML::Node switchesNode; + this->InstallerType = defaultInstaller.InstallerType; + this->Scope = "user"; - if (installerNode["SignatureSha256"]) + const std::vector<ManifestFieldInfo> InstallerFieldInfos = { - this->SignatureSha256 = Utility::SHA256::ConvertToBytes(installerNode["SignatureSha256"].as<std::string>()); - } + { "Arch", [this](const YAML::Node& value) { Arch = Utility::ConvertToArchitectureEnum(value.as<std::string>()); }, true }, + { "Url", [this](const YAML::Node& value) { Url = value.as<std::string>(); }, true }, + { "Sha256", [this](const YAML::Node& value) { Sha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); }, true, "^[A-Fa-f0-9]{64}$" }, + { "SignatureSha256", [this](const YAML::Node& value) { SignatureSha256 = Utility::SHA256::ConvertToBytes(value.as<std::string>()); } }, + { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); } }, + { "Scope", [this](const YAML::Node& value) { Scope = value.as<std::string>(); } }, + { "InstallerType", [this](const YAML::Node& value) { InstallerType = ConvertToInstallerTypeEnum(value.as<std::string>()); } }, + { "Switches", [&](const YAML::Node& value) { switchesNode = value; } }, + }; - // Optional fields. - this->Language = installerNode["Language"] ? installerNode["Language"].as<std::string>() : ""; - this->Scope = installerNode["Scope"] ? installerNode["Scope"].as<std::string>() : ""; + auto resultErrors = ValidateAndProcessFields(installerNode, InstallerFieldInfos, fullValidation); - this->InstallerType = installerNode["InstallerType"] ? - ConvertToInstallerTypeEnum(installerNode["InstallerType"].as<std::string>()) : - defaultInstaller.InstallerType; + // Populate default known switches + this->Switches = GetDefaultKnownSwitches(this->InstallerType); - std::map<InstallerSwitchType, string_t> defaultKnownSwitches = GetDefaultKnownSwitches(this->InstallerType); + // Override with switches from manifest root if applicable + for (auto const& keyValuePair : defaultInstaller.Switches) + { + this->Switches[keyValuePair.first] = keyValuePair.second; + } - if (installerNode["Switches"]) + // Override with switches from installer if applicable + if (!switchesNode.IsNull()) { - YAML::Node switchesNode = installerNode["Switches"]; - PopulateSwitchesFields(&switchesNode, this->Switches, &(defaultInstaller.Switches), &defaultKnownSwitches); + auto errors = PopulateSwitchesFields(switchesNode, this->Switches, fullValidation); + std::move(errors.begin(), errors.end(), std::inserter(resultErrors, resultErrors.end())); } - else + + // Extra semantic validations after basic validation and field population + if (fullValidation) { - PopulateSwitchesFields(nullptr, this->Switches, &(defaultInstaller.Switches), &defaultKnownSwitches); + if (Arch == Utility::Architecture::Unknown) + { + resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Arch"); + } + + if (InstallerType == InstallerTypeEnum::Unknown) + { + resultErrors.emplace_back(ManifestError::InvalidFieldValue, "InstallerType"); + } + + if (InstallerType == InstallerTypeEnum::Exe && + (Switches.find(InstallerSwitchType::SilentWithProgress) == Switches.end() || + Switches.find(InstallerSwitchType::Silent) == Switches.end())) + { + resultErrors.emplace_back(ManifestError::ExeInstallerMissingSilentSwitches); + } + + if (IsValidURL(NULL, Utility::ConvertToUTF16(Url).c_str(), 0) == S_FALSE) + { + resultErrors.emplace_back(ManifestError::InvalidFieldValue, "Url", Url); + } } - } - void ManifestInstaller::PopulateSwitchesFields( - const YAML::Node* switchesNode, - std::map<InstallerSwitchType, string_t>& switches, - const std::map<InstallerSwitchType, string_t>* manifestRootSwitches, - const std::map<InstallerSwitchType, string_t>* defaultKnownSwitches) - { - PopulateOneSwitchField(switchesNode, "Custom", InstallerSwitchType::Custom, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "Silent", InstallerSwitchType::Silent, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "SilentWithProgress", InstallerSwitchType::SilentWithProgress, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "Interactive", InstallerSwitchType::Interactive, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "Language", InstallerSwitchType::Language, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "Log", InstallerSwitchType::Log, switches, manifestRootSwitches, defaultKnownSwitches); - PopulateOneSwitchField(switchesNode, "InstallLocation", InstallerSwitchType::InstallLocation, switches, manifestRootSwitches, defaultKnownSwitches); + return resultErrors; } - void ManifestInstaller::PopulateOneSwitchField( - const YAML::Node* switchesNode, - const std::string& switchName, - InstallerSwitchType switchType, + std::vector<ValidationError> ManifestInstaller::PopulateSwitchesFields( + const YAML::Node& switchesNode, std::map<InstallerSwitchType, string_t>& switches, - const std::map<InstallerSwitchType, string_t>* manifestRootSwitches, - const std::map<InstallerSwitchType, string_t>* defaultKnownSwitches) + bool fullValidation) { - if (switchesNode && (*switchesNode)[switchName]) - { - switches.emplace(switchType, (*switchesNode)[switchName].as<std::string>()); - } - else if (manifestRootSwitches && manifestRootSwitches->find(switchType) != manifestRootSwitches->end()) - { - switches.emplace(switchType, manifestRootSwitches->at(switchType)); - } - else if (defaultKnownSwitches && defaultKnownSwitches->find(switchType) != defaultKnownSwitches->end()) - { - switches.emplace(switchType, defaultKnownSwitches->at(switchType)); - } + const std::vector<ManifestFieldInfo> SwitchesFieldInfos = + { + { "Custom", [&](const YAML::Node& value) { switches[InstallerSwitchType::Custom] = value.as<std::string>(); } }, + { "Silent", [&](const YAML::Node& value) { switches[InstallerSwitchType::Silent] = value.as<std::string>(); } }, + { "SilentWithProgress", [&](const YAML::Node& value) { switches[InstallerSwitchType::SilentWithProgress] = value.as<std::string>(); } }, + { "Interactive", [&](const YAML::Node& value) { switches[InstallerSwitchType::Interactive] = value.as<std::string>(); } }, + { "Language", [&](const YAML::Node& value) { switches[InstallerSwitchType::Language] = value.as<std::string>(); } }, + { "Log", [&](const YAML::Node& value) { switches[InstallerSwitchType::Log] = value.as<std::string>(); } }, + { "InstallLocation", [&](const YAML::Node& value) { switches[InstallerSwitchType::InstallLocation] = value.as<std::string>(); } }, + }; + + return ValidateAndProcessFields(switchesNode, SwitchesFieldInfos, fullValidation); } std::map<ManifestInstaller::InstallerSwitchType, ManifestInstaller::string_t> ManifestInstaller::GetDefaultKnownSwitches(InstallerTypeEnum installerType) diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.h b/src/AppInstallerRepositoryCore/Manifest/ManifestInstaller.h @@ -8,6 +8,8 @@ #include <string> #include <map> +#include "ManifestValidation.h" + namespace AppInstaller::Manifest { using namespace std::string_view_literals; @@ -16,9 +18,8 @@ namespace AppInstaller::Manifest static constexpr std::string_view ARG_TOKEN_LOGPATH = "<LOGPATH>"sv; static constexpr std::string_view ARG_TOKEN_INSTALLPATH = "<INSTALLPATH>"sv; - class ManifestInstaller + struct ManifestInstaller { - public: using string_t = Utility::NormalizedString; enum class InstallerTypeEnum @@ -76,25 +77,14 @@ namespace AppInstaller::Manifest // Populates InstallerSwitches // The value declared in the manifest takes precedence, then value in the manifest root, then default known values. - static void PopulateSwitchesFields( - const YAML::Node* switchesNode, - std::map<InstallerSwitchType, string_t>& switches, - const std::map<InstallerSwitchType, string_t>* manifestRootSwitches = nullptr, - const std::map<InstallerSwitchType, string_t>* defaultKnownSwitches = nullptr); - - // Populates one Installer Switch - // The value declared in the manifest takes precedence, then value in the manifest root, then default known values. - static void PopulateOneSwitchField( - const YAML::Node* switchesNode, - const std::string& switchName, - InstallerSwitchType switchType, + static std::vector<ValidationError> PopulateSwitchesFields( + const YAML::Node& switchesNode, std::map<InstallerSwitchType, string_t>& switches, - const std::map<InstallerSwitchType, string_t>* manifestRootSwitches, - const std::map<InstallerSwitchType, string_t>* defaultKnownSwitches); + bool fullValidation); // Populates ManifestInstaller // defaultInstaller: if an optional field is not found in the YAML node, the field will be populated with value from defaultInstaller. - void PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller); + std::vector<ValidationError> PopulateInstallerFields(const YAML::Node& installerNode, const ManifestInstaller& defaultInstaller, bool fullValidation); static std::string InstallerTypeToString(InstallerTypeEnum installerType); }; diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.cpp @@ -6,22 +6,21 @@ namespace AppInstaller::Manifest { - void ManifestLocalization::PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization) + std::vector<ValidationError> ManifestLocalization::PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization, bool fullValidation) { - // Required - this->Language = localizationNode["Language"].as<std::string>(); + // Populates default values first + this->Description = defaultLocalization.Description; + this->Homepage = defaultLocalization.Homepage; + this->LicenseUrl = defaultLocalization.LicenseUrl; - // Optional - this->Description = localizationNode["Description"] ? - string_t(localizationNode["Description"].as<std::string>()) : - defaultLocalization.Description; + const std::vector<ManifestFieldInfo> FieldInfos = + { + { "Language", [this](const YAML::Node& value) { Language = value.as<std::string>(); }, true }, + { "Description", [this](const YAML::Node& value) { Description = value.as<std::string>(); } }, + { "Homepage", [this](const YAML::Node& value) { Homepage = value.as<std::string>(); } }, + { "LicenseUrl", [this](const YAML::Node& value) { LicenseUrl = value.as<std::string>(); } }, + }; - this->Homepage = localizationNode["Homepage"] ? - string_t(localizationNode["Homepage"].as<std::string>()) : - defaultLocalization.Homepage; - - this->LicenseUrl = localizationNode["LicenseUrl"] ? - string_t(localizationNode["LicenseUrl"].as<std::string>()) : - defaultLocalization.LicenseUrl; + return ValidateAndProcessFields(localizationNode, FieldInfos, fullValidation); } } diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.h b/src/AppInstallerRepositoryCore/Manifest/ManifestLocalization.h @@ -3,7 +3,7 @@ #pragma once #include <string> -namespace YAML { class Node; } +#include "ManifestValidation.h" namespace AppInstaller::Manifest { @@ -23,6 +23,6 @@ namespace AppInstaller::Manifest // Populates ManifestLocalization // defaultLocalization: if an optional field is not found in the YAML node, the field will be populated with value from defaultLocalization. - void PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization); + std::vector<ValidationError> PopulateLocalizationFields(const YAML::Node& localizationNode, const ManifestLocalization& defaultLocalization, bool fullValidation); }; } \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.cpp b/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "ManifestValidation.h" + +namespace AppInstaller::Manifest +{ + std::vector<ValidationError> ValidateAndProcessFields(const YAML::Node& rootNode, const std::vector<ManifestFieldInfo> fieldInfos, bool fullValidation) + { + std::vector<ValidationError> errors; + + if (rootNode.size() == 0) + { + errors.emplace_back(ManifestError::InvalidRootNode, "", "", rootNode.Mark().line, rootNode.Mark().column); + return errors; + } + + // Keeps track of already processed fields. Used to check duplicate fields or missing required fields. + std::set<std::string> processedFields; + + for (auto const& keyValuePair : rootNode) + { + std::string key = keyValuePair.first.as<std::string>(); + YAML::Node valueNode = keyValuePair.second; + + // We'll do case insensitive search first and validate correct case later. + auto fieldIter = std::find_if(fieldInfos.begin(), fieldInfos.end(), + [&](auto const& s) + { + return Utility::CaseInsensitiveEquals(s.Name, key); + }); + + if (fieldIter != fieldInfos.end()) + { + ManifestFieldInfo fieldInfo = *fieldIter; + + // Make sure the found key is in Pascal Case + if (key != fieldInfo.Name) + { + errors.emplace_back(ManifestError::FieldIsNotPascalCase, key, "", keyValuePair.first.Mark().line, keyValuePair.first.Mark().column); + } + + // Make sure it's not a duplicate key + if (!processedFields.insert(fieldInfo.Name).second) + { + errors.emplace_back(ManifestError::FieldDuplicate, fieldInfo.Name, "", keyValuePair.first.Mark().line, keyValuePair.first.Mark().column); + } + + // Validate non empty value is provided for required fields + if (fieldInfo.Required) + { + if (!valueNode.IsDefined() || valueNode.IsNull() || // Should be defined and not null + (valueNode.IsScalar() && valueNode.as<std::string>().empty()) || // Scalar type should have content + ((valueNode.IsMap() || valueNode.IsSequence()) && valueNode.size() == 0)) // Map or sequence type should have size greater than 0 + { + errors.emplace_back(ManifestError::RequiredFieldEmpty, fieldInfo.Name, "", valueNode.Mark().line, valueNode.Mark().column); + } + } + + // Validate value against regex if applicable + if (fullValidation && !fieldInfo.RegEx.empty()) + { + std::string value = valueNode.as<std::string>(); + std::regex pattern{ fieldInfo.RegEx }; + if (!std::regex_match(value, pattern)) + { + errors.emplace_back(ManifestError::InvalidFieldValue, fieldInfo.Name, value, valueNode.Mark().line, valueNode.Mark().column); + continue; + } + } + + if (!valueNode.IsNull()) + { + fieldInfo.ProcessFunc(valueNode); + } + } + else + { + // Forward compatibility for future fields + if (fullValidation) + { + errors.emplace_back(ManifestError::FieldUnknown, key, "", keyValuePair.first.Mark().line, keyValuePair.first.Mark().column); + } + } + } + + // Make sure required fields are provided + for (auto const& fieldInfo : fieldInfos) + { + if (fieldInfo.Required && processedFields.find(fieldInfo.Name) == processedFields.end()) + { + errors.emplace_back(ManifestError::RequiredFieldMissing, fieldInfo.Name); + } + } + + return errors; + } +}+ \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.h b/src/AppInstallerRepositoryCore/Manifest/ManifestValidation.h @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +#include <string> +#include <functional> +#include <wil/result.h> +#include <AppInstallerErrors.h> + +namespace YAML { class Node; } + +namespace AppInstaller::Manifest +{ + namespace ManifestError + { + const char* const InvalidRootNode = "Manifest: Encountered unexpected root node."; + const char* const FieldUnknown = "Manifest: Unknown field."; + const char* const FieldIsNotPascalCase = "Manifest: All field names should be PascalCased."; + const char* const FieldDuplicate = "Manifest: Duplicate field found in the manifest."; + const char* const RequiredFieldEmpty = "Manifest: Required field with empty value."; + const char* const RequiredFieldMissing = "Manifest: Required field missing."; + const char* const InvalidFieldValue = "Manifest: Invalid field value."; + const char* const ExeInstallerMissingSilentSwitches = "Manifest: Silent switches are required for InstallerType exe."; + const char* const FieldNotSupported = "Manifest: Field is not supported."; + const char* const DuplicateInstallerEntry = "Manifest: Duplicate installer entry found."; + } + + struct ValidationError + { + std::string Message; + std::string Field; + std::string Value; + int Line; + int Column; + + ValidationError(std::string message, std::string field = {}, std::string value = {}, int line = -1, int column = -1) : + Message(std::move(message)), Field(std::move(field)), Value(std::move(value)), Line(line), Column(column) {} + }; + + // This struct contains individual app manifest field info + struct ManifestFieldInfo + { + std::string Name; + std::function<void(const YAML::Node&)> ProcessFunc; + bool Required = false; + std::string RegEx = {}; + }; + + // This method takes YAML root node and list of manifest field info. + // Yaml-cpp does not support case insensitive search and it allows duplicate keys. If duplicate keys exist, + // the value is undefined. So in this method, we will iterate through the node map and process each individual + // pair ourselves. This also helps with generating aggregated error rather than throwing on first failure. + std::vector<ValidationError> ValidateAndProcessFields(const YAML::Node& rootNode, const std::vector<ManifestFieldInfo> fieldInfos, bool fullValidation); + + struct ManifestException : public wil::ResultException + { + ManifestException(std::vector<ValidationError>&& errors = {}) : + m_errors(std::move(errors)), wil::ResultException(APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) {} + + // Error message without wil diagnostic info + const std::string& GetManifestErrorMessage() const noexcept + { + if (m_manifestErrorMessage.empty()) + { + if (m_errors.empty()) + { + // Syntax error, Yaml-cpp error is stored in FailureInfo + m_manifestErrorMessage = Utility::ConvertToUTF8(GetFailureInfo().pszMessage); + } + else + { + for (auto const& error : m_errors) + { + m_manifestErrorMessage += error.Message; + if (!error.Field.empty()) + { + m_manifestErrorMessage += " Field: " + error.Field; + } + if (!error.Value.empty()) + { + m_manifestErrorMessage += " Value: " + error.Value; + } + if (error.Line >= 0 && error.Column >= 0) + { + m_manifestErrorMessage += " Line: " + std::to_string(error.Line) + ", Column: " + std::to_string(error.Column); + } + m_manifestErrorMessage += '\n'; + } + } + } + return m_manifestErrorMessage; + } + + const char* what() const noexcept override + { + if (m_whatMessage.empty()) + { + m_whatMessage = ResultException::what(); + + if (!m_errors.empty()) + { + m_whatMessage += GetManifestErrorMessage(); + } + } + return m_whatMessage.c_str(); + } + + private: + std::vector<ValidationError> m_errors; + mutable std::string m_whatMessage; + mutable std::string m_manifestErrorMessage; + }; +}+ \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h b/src/AppInstallerRepositoryCore/Public/AppInstallerRepositorySearch.h @@ -72,6 +72,8 @@ namespace AppInstaller::Repository // A single application result from a search. struct IApplication { + virtual ~IApplication() = default; + // Gets the id of the application. virtual std::string GetId() = 0; diff --git a/src/AppInstallerRepositoryCore/pch.h b/src/AppInstallerRepositoryCore/pch.h @@ -4,6 +4,7 @@ #define NOMINMAX #include <windows.h> +#include <urlmon.h> #include <AppInstallerDateTime.h> #include <AppInstallerDeployment.h> @@ -41,6 +42,8 @@ #include <tuple> #include <type_traits> #include <utility> +#include <functional> +#include <regex> #ifndef AICLI_DISABLE_TEST_HOOKS #include <functional> diff --git a/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.h b/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.h @@ -10,6 +10,9 @@ extern "C" // A string taken in by the utility; in UTF16. typedef wchar_t const* const APPINSTALLER_SQLITE_INDEX_STRING; + // A string returned by the utility; in UTF16. + typedef BSTR APPINSTALLER_SQLITE_INDEX_STRING_OUT; + #define APPINSTALLER_SQLITE_INDEX_API HRESULT __stdcall #define APPINSTALLER_SQLITE_INDEX_VERSION_LATEST ((UINT32)-1) @@ -64,4 +67,11 @@ extern "C" // Removes data that is no longer needed for an index that is to be published. APPINSTALLER_SQLITE_INDEX_API AppInstallerSQLiteIndexPrepareForPackaging( APPINSTALLER_SQLITE_INDEX_HANDLE index); + + // Validates a given manifest. Returns a bool for validation result and + // a string representing validation errors if validation failed. + APPINSTALLER_SQLITE_INDEX_API AppInstallerValidateManifest( + APPINSTALLER_SQLITE_INDEX_STRING manifestPath, + bool* succeeded, + APPINSTALLER_SQLITE_INDEX_STRING_OUT* failureMessage); } diff --git a/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.vcxproj b/src/AppInstallerSQLiteIndexUtil/AppInstallerSQLiteIndexUtil.vcxproj @@ -117,7 +117,7 @@ <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <WarningLevel>Level4</WarningLevel> - <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions> + <AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /D _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING</AdditionalOptions> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> @@ -139,9 +139,9 @@ <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Source.def</ModuleDefinitionFile> <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Source.def</ModuleDefinitionFile> <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Source.def</ModuleDefinitionFile> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> @@ -153,7 +153,7 @@ <Link> <SubSystem Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Windows</SubSystem> <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Source.def</ModuleDefinitionFile> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> @@ -183,10 +183,10 @@ <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Source.def</ModuleDefinitionFile> <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Source.def</ModuleDefinitionFile> <ModuleDefinitionFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Source.def</ModuleDefinitionFile> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">wininet.lib;shell32.lib;winsqlite3.lib;shlwapi.lib;icuuc.lib;icuin.lib;urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/AppInstallerSQLiteIndexUtil/Exports.cpp b/src/AppInstallerSQLiteIndexUtil/Exports.cpp @@ -4,6 +4,7 @@ #include "AppInstallerSQLiteIndexUtil.h" using namespace AppInstaller::Utility; +using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository::Microsoft; extern "C" @@ -147,4 +148,30 @@ extern "C" return S_OK; } CATCH_RETURN() + + APPINSTALLER_SQLITE_INDEX_API AppInstallerValidateManifest( + APPINSTALLER_SQLITE_INDEX_STRING manifestPath, + bool* succeeded, + APPINSTALLER_SQLITE_INDEX_STRING_OUT* failureMessage) try + { + THROW_HR_IF(E_INVALIDARG, !manifestPath); + THROW_HR_IF(E_INVALIDARG, !succeeded); + + try + { + (void)Manifest::CreateFromPath(manifestPath, true); + *succeeded = true; + } + catch (const ManifestException& e) + { + *succeeded = false; + if (failureMessage) + { + *failureMessage = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str()); + } + } + + return S_OK; + } + CATCH_RETURN() } diff --git a/src/AppInstallerSQLiteIndexUtil/Source.def b/src/AppInstallerSQLiteIndexUtil/Source.def @@ -9,3 +9,4 @@ EXPORTS AppInstallerSQLiteIndexUpdateManifest AppInstallerSQLiteIndexRemoveManifest AppInstallerSQLiteIndexPrepareForPackaging + AppInstallerValidateManifest diff --git a/src/AppInstallerSQLiteIndexUtil/pch.h b/src/AppInstallerSQLiteIndexUtil/pch.h @@ -12,6 +12,7 @@ #include <Public/AppInstallerLogging.h> #include <Public/AppInstallerTelemetry.h> #include <Microsoft/SQLiteIndex.h> +#include <Manifest/Manifest.h> #include <wil/result_macros.h>