commit 1e189c57d260ab0e68424debda71fb6547cdb439 parent 595bb4707c61ef68e30f4a6c0443c6862d17429c Author: JohnMcPMS <johnmcp@microsoft.com> Date: Fri, 20 Dec 2019 15:39:35 -0800 Add the basis for the SQLite index (#15) Diffstat:
42 files changed, 1265 insertions(+), 409 deletions(-)
diff --git a/README.md b/README.md @@ -1,3 +1,4 @@ +[](https://dev.azure.com/ms/pkg/_build/latest?definitionId=308&branchName=master) # Contributing @@ -14,7 +15,7 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft. contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -Data/Telemetry +# Data/Telemetry -This project collects usage data and sends it to Microsoft to help improve our products and services. See the privacy statement for more details. +This project collects usage data and sends it to Microsoft to help improve our products and services. See the [privacy statement](privacy.md) for more details. diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -34,8 +34,8 @@ steps: - task: CmdLine@2 inputs: script: | - RepositoryLibTests.exe -s -r junit -o TEST-RepositoryLib-$(_artifact).xml - workingDirectory: 'src\Release\' + AppInstallerClientTests.exe -s -r junit -o TEST-AppInstallerClient-$(_artifact).xml + workingDirectory: 'src\x64\Release\AppInstallerClientTests\' - task: PublishTestResults@2 inputs: diff --git a/src/AppInstallerClient.sln b/src/AppInstallerClient.sln @@ -16,14 +16,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RepositoryLib", "Repository EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project", "Project", "{8D53D749-D51C-46F8-A162-9371AAA6C2E7}" ProjectSection(SolutionItems) = preProject + ..\azure-pipelines.yml = ..\azure-pipelines.yml ..\cgmanifest.json = ..\cgmanifest.json + ..\README.md = ..\README.md EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "catch2", "catch2\catch2.vcxitems", "{5295E21E-9868-4DE2-A177-FBB97B36579B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{63D9DA93-A735-4CBC-8127-343EB2E58E43}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RepositoryLibTests", "RepositoryLibTests\RepositoryLibTests.vcxproj", "{89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerClientTests", "AppInstallerClientTests\AppInstallerClientTests.vcxproj", "{89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YamlCppLib", "YamlCppLib\YamlCppLib.vcxproj", "{8BB94BB8-374F-4294-BCA1-C7811514A6B7}" EndProject @@ -191,7 +191,6 @@ Global GlobalSection(NestedProjects) = preSolution {4B988559-DDBD-4456-9300-0C3AAD12C466} = {60618CAC-2995-4DF9-9914-45C6FC02C995} {5295E21E-9868-4DE2-A177-FBB97B36579B} = {60618CAC-2995-4DF9-9914-45C6FC02C995} - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230} = {63D9DA93-A735-4CBC-8127-343EB2E58E43} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B6FDB70C-A751-422C-ACD1-E35419495857} diff --git a/src/AppInstallerClientTests/AppInstallerClientTests.vcxproj b/src/AppInstallerClientTests/AppInstallerClientTests.vcxproj @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props')" /> + <PropertyGroup Label="Globals"> + <CppWinRTOptimized>true</CppWinRTOptimized> + <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge> + <CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata> + <MinimalCoreWin>true</MinimalCoreWin> + <VCProjectVersion>15.0</VCProjectVersion> + <ProjectGuid>{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>AppInstallerClientTests</RootNamespace> + <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion> + <ProjectName>AppInstallerClientTests</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v140</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <LinkIncremental>false</LinkIncremental> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + <Import Project="..\catch2\catch2.vcxitems" Label="Shared" /> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="PropertySheet.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <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> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateWindowsMetadata>false</GenerateWindowsMetadata> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> + <ClCompile> + <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateWindowsMetadata>false</GenerateWindowsMetadata> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="pch.h" /> + <ClInclude Include="TestCommon.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="PackageManifestHelper.cpp" /> + <ClCompile Include="main.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="SQLiteIndex.cpp" /> + <ClCompile Include="SQLiteWrapper.cpp" /> + <ClCompile Include="TestCommon.cpp" /> + </ItemGroup> + <ItemGroup> + <CopyFileToFolders Include="TestData\GoodManifest.yml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> + <DeploymentContent>true</DeploymentContent> + </CopyFileToFolders> + <None Include="packages.config" /> + <None Include="PropertySheet.props" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\PackageManifestHelper\PackageManifestHelper.vcxproj"> + <Project>{f8f75341-9455-4d5b-824a-47a7818ee864}</Project> + </ProjectReference> + <ProjectReference Include="..\RepositoryLib\RepositoryLib.vcxproj"> + <Project>{5eb88068-5fb9-4e69-89b2-72dbc5e068f9}</Project> + </ProjectReference> + <ProjectReference Include="..\YamlCppLib\YamlCppLib.vcxproj"> + <Project>{8bb94bb8-374f-4294-bca1-c7811514a6b7}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets')" /> + <Import Project="$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.191107.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.191107.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props'))" /> + <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets'))" /> + <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.191107.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.191107.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> + </Target> +</Project>+ \ No newline at end of file diff --git a/src/AppInstallerClientTests/AppInstallerClientTests.vcxproj.filters b/src/AppInstallerClientTests/AppInstallerClientTests.vcxproj.filters @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="TestData"> + <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="TestCommon.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteWrapper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SQLiteIndex.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TestCommon.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="PackageManifestHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <None Include="PropertySheet.props" /> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + <CopyFileToFolders Include="TestData\GoodManifest.yml"> + <Filter>TestData</Filter> + </CopyFileToFolders> + </ItemGroup> +</Project>+ \ No newline at end of file diff --git a/src/RepositoryLibTests/PackageManifestHelper.cpp b/src/AppInstallerClientTests/PackageManifestHelper.cpp diff --git a/src/RepositoryLibTests/PropertySheet.props b/src/AppInstallerClientTests/PropertySheet.props diff --git a/src/AppInstallerClientTests/SQLiteIndex.cpp b/src/AppInstallerClientTests/SQLiteIndex.cpp @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include <Microsoft/SQLiteIndex.h> + +using namespace AppInstaller::Repository::Microsoft; + +TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") +{ + TestCommon::TempFile tempFile{ "repolibtest_tempdb", ".db" }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + Schema::Version versionCreated; + + // Create the index + { + SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, Schema::Version::Latest()); + versionCreated = index.GetVersion(); + } + + // Reopen the index for read only + { + INFO("Trying with Read"); + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::Read); + Schema::Version versionRead = index.GetVersion(); + REQUIRE(versionRead == versionCreated); + } + + // Reopen the index for read/write + { + INFO("Trying with ReadWrite"); + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::ReadWrite); + Schema::Version versionRead = index.GetVersion(); + REQUIRE(versionRead == versionCreated); + } + + // Reopen the index for immutable read + { + INFO("Trying with Immutable"); + SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteIndex::OpenDisposition::Immutable); + Schema::Version versionRead = index.GetVersion(); + REQUIRE(versionRead == versionCreated); + } +} diff --git a/src/AppInstallerClientTests/SQLiteWrapper.cpp b/src/AppInstallerClientTests/SQLiteWrapper.cpp @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include <SQLiteWrapper.h> + +using namespace AppInstaller::Repository::SQLite; + +#define SQLITE_MEMORY_DB_CONNECTION_TARGET ":memory:" + +static const char* s_CreateSimpleTestTableSQL = R"( +CREATE TABLE [main].[simpletest]( + [first] INT, + [second] TEXT); +)"; + +static const char* s_insertToSimpleTestTableSQL = R"( +insert into simpletest (first, second) values (?, ?) +)"; + +static const char* s_selectFromSimpleTestTableSQL = R"( +select first, second from simpletest +)"; + +void CreateSimpleTestTable(Connection& connection) +{ + Statement createTable = Statement::Create(connection, s_CreateSimpleTestTableSQL); + REQUIRE_FALSE(createTable.Step()); + REQUIRE(createTable.GetState() == Statement::State::Completed); +} + +void InsertIntoSimpleTestTable(Connection& connection, int firstVal, const std::string& secondVal) +{ + Statement insert = Statement::Create(connection, s_insertToSimpleTestTableSQL); + + insert.Bind(1, firstVal); + insert.Bind(2, secondVal); + + REQUIRE_FALSE(insert.Step()); + REQUIRE(insert.GetState() == Statement::State::Completed); +} + +void SelectFromSimpleTestTableOnlyOneRow(Connection& connection, int firstVal, const std::string& secondVal) +{ + Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); + REQUIRE(select.Step()); + REQUIRE(select.GetState() == Statement::State::HasRow); + + int firstRead = select.GetColumn<int>(0); + std::string secondRead = select.GetColumn<std::string>(1); + + REQUIRE(firstVal == firstRead); + REQUIRE(secondVal == secondRead); + + auto tuple = select.GetRow<int, std::string>(); + + REQUIRE(firstVal == std::get<0>(tuple)); + REQUIRE(secondVal == std::get<1>(tuple)); + + REQUIRE_FALSE(select.Step()); + REQUIRE(select.GetState() == Statement::State::Completed); + + select.Reset(); + REQUIRE(select.GetState() == Statement::State::Prepared); + + REQUIRE(select.Step()); + REQUIRE(select.GetState() == Statement::State::HasRow); +} + +TEST_CASE("SQLiteWrapperMemoryCreate", "[sqlitewrapper]") +{ + Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); + + CreateSimpleTestTable(connection); + + int firstVal = 1; + std::string secondVal = "test"; + + InsertIntoSimpleTestTable(connection, firstVal, secondVal); + + SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); +} + +TEST_CASE("SQLiteWrapperFileCreateAndReopen", "[sqlitewrapper]") +{ + TestCommon::TempFile tempFile{ "repolibtest_tempdb", ".db" }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + int firstVal = 1; + std::string secondVal = "test"; + + // Create the DB and some data + { + Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::Create); + + CreateSimpleTestTable(connection); + + InsertIntoSimpleTestTable(connection, firstVal, secondVal); + } + + // Reopen the DB and read data + { + Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); + + SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); + } +} + +TEST_CASE("SQLiteWrapperSavepointRollback", "[sqlitewrapper]") +{ + Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); + + int firstVal = 1; + std::string secondVal = "test"; + + CreateSimpleTestTable(connection); + + Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); + + InsertIntoSimpleTestTable(connection, firstVal, secondVal); + + savepoint.Rollback(); + + Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); + REQUIRE(!select.Step()); + REQUIRE(select.GetState() == Statement::State::Completed); +} + +TEST_CASE("SQLiteWrapperSavepointRollbackOnDestruct", "[sqlitewrapper]") +{ + Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); + + int firstVal = 1; + std::string secondVal = "test"; + + CreateSimpleTestTable(connection); + + { + Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); + + InsertIntoSimpleTestTable(connection, firstVal, secondVal); + } + + Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); + REQUIRE(!select.Step()); + REQUIRE(select.GetState() == Statement::State::Completed); +} + +TEST_CASE("SQLiteWrapperSavepointCommit", "[sqlitewrapper]") +{ + Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); + + int firstVal = 1; + std::string secondVal = "test"; + + CreateSimpleTestTable(connection); + + { + Savepoint savepoint = Savepoint::Create(connection, "test_savepoint"); + + InsertIntoSimpleTestTable(connection, firstVal, secondVal); + + savepoint.Commit(); + } + + SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); +} diff --git a/src/AppInstallerClientTests/TestCommon.cpp b/src/AppInstallerClientTests/TestCommon.cpp @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" + +namespace TestCommon +{ + namespace + { + struct initRand + { + initRand() { srand(static_cast<unsigned int>(time(NULL))); } + }; + + inline int getRand() + { + static initRand srandHolder; + return rand(); + } + + inline std::string GetTempFilePath(const std::string& baseName, const std::string& baseExt) + { + char tempPath[MAX_PATH]{}; + REQUIRE(GetTempPathA(MAX_PATH, tempPath) != 0); + + srand(static_cast<unsigned int>(time(NULL))); + std::stringstream tempFileName; + tempFileName << tempPath << '\\' << baseName << getRand() << baseExt; + + return tempFileName.str(); + } + + static bool s_TempFileDestructorKeepsFile{}; + } + + TempFile::TempFile(const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction) + { + _filepath = GetTempFilePath(baseName, baseExt); + if (deleteFileOnConstruction) + { + DeleteFileA(_filepath.c_str()); + } + } + + TempFile::~TempFile() + { + if (!s_TempFileDestructorKeepsFile) + { + DeleteFileA(_filepath.c_str()); + } + } + + void TempFile::SetDestructorBehavior(bool keepFilesOnDestruction) + { + s_TempFileDestructorKeepsFile = keepFilesOnDestruction; + } +} diff --git a/src/AppInstallerClientTests/TestCommon.h b/src/AppInstallerClientTests/TestCommon.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "pch.h" +#include <string> + +namespace TestCommon +{ + struct TempFile + { + TempFile(const std::string& baseName, const std::string& baseExt, bool deleteFileOnConstruction = true); + + TempFile(const TempFile&) = delete; + TempFile& operator=(const TempFile&) = delete; + + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + ~TempFile(); + + const std::string& GetPath() const { return _filepath; } + operator const std::string& () const { return _filepath; } + + static void SetDestructorBehavior(bool keepFilesOnDestruction); + + private: + std::string _filepath; + }; +} diff --git a/src/RepositoryLibTests/TestData/BadManifest-MissingName.yml b/src/AppInstallerClientTests/TestData/BadManifest-MissingName.yml diff --git a/src/RepositoryLibTests/TestData/GoodManifest.yml b/src/AppInstallerClientTests/TestData/GoodManifest.yml diff --git a/src/AppInstallerClientTests/main.cpp b/src/AppInstallerClientTests/main.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#define CATCH_CONFIG_RUNNER +#include <catch.hpp> +#include <winrt/Windows.Foundation.h> +#include <string> +#include <vector> + +#include "TestCommon.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace std::string_literals; + +int main(int argc, char** argv) +{ + init_apartment(); + + std::vector<char*> args; + for (int i = 0; i < argc; ++i) + { + if ("-ktf"s == argv[i]) + { + TestCommon::TempFile::SetDestructorBehavior(true); + } + else + { + args.push_back(argv[i]); + } + } + + return Catch::Session().run(static_cast<int>(args.size()), args.data()); +} diff --git a/src/AppInstallerClientTests/packages.config b/src/AppInstallerClientTests/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Windows.CppWinRT" version="2.0.191202.6" targetFramework="native" /> + <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.191107.2" targetFramework="native" /> +</packages>+ \ No newline at end of file diff --git a/src/RepositoryLibTests/pch.cpp b/src/AppInstallerClientTests/pch.cpp diff --git a/src/AppInstallerClientTests/pch.h b/src/AppInstallerClientTests/pch.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#define NOMINMAX +#include <Windows.h> + +#include <catch.hpp> + +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> + +#include <iostream> +#include <sstream> +#include <utility> + +#include <yaml-cpp/yaml.h>+ \ No newline at end of file diff --git a/src/PackageManifestHelper/Manifest.cpp b/src/PackageManifestHelper/Manifest.cpp @@ -78,9 +78,9 @@ namespace AppInstaller::Manifest YAML::Node rootNode = YAML::Load(input); manifest.PopulateManifestFields(rootNode); } - catch (std::exception & e) + catch (std::exception&) { - // Log theinput string when read manifest failure + // TODO: Log theinput string when read manifest failure throw; } diff --git a/src/PackageManifestHelper/PackageManifestHelper.vcxproj b/src/PackageManifestHelper/PackageManifestHelper.vcxproj @@ -77,6 +77,38 @@ <WholeProgramOptimization>true</WholeProgramOptimization> <LinkIncremental>false</LinkIncremental> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> <ClCompile> <PrecompiledHeader>Use</PrecompiledHeader> diff --git a/src/RepositoryLib/Microsoft/README.md b/src/RepositoryLib/Microsoft/README.md @@ -0,0 +1,9 @@ +### Design +The object stack is as such: +- SQLiteIndex :: Houses the database connection and the proper interface with which to interact with it +- ISQLiteIndex :: Interface that creates a uniform model to use against all schemas +- Schema::V*::Interface :: Actual implementation of ISQLiteIndex for specific schema version + +The code that needs to interact with an index will create a SQLiteIndex object. That in turn will open the database, determine the schema version, then create the appropriate ISQLiteIndex providing object. All queries and changes to the index will go through the SQLiteIndex object. + +When a change to the schema is needed, a new schema directory should be created. This can pull code from the schemas before it, only updating the specific table(s) that are needed. Then Version code should be updated to create the new Interface as appropriate. Any new methods needed on ISQLiteIndex should be added, and SQLiteIndexBase to implement them in terms of the older functions. Only the new schema should need to implement the new functions. **Once shipped, one should never need to update code within an existing schema, save for bug fixes.** diff --git a/src/RepositoryLib/Microsoft/SQLiteIndex.cpp b/src/RepositoryLib/Microsoft/SQLiteIndex.cpp @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "SQLiteIndex.h" + +#include "Schema/MetadataTable.h" + +namespace AppInstaller::Repository::Microsoft +{ + SQLiteIndex SQLiteIndex::CreateNew(const std::string& filePath, Schema::Version version) + { + SQLiteIndex result{ filePath, version }; + + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(result._dbconn, "sqliteindex_createnew"); + + Schema::MetadataTable::Create(result._dbconn); + // Use calculated version, as incoming version could be 'latest' + result._version.SetSchemaVersion(result._dbconn); + + savepoint.Commit(); + + return result; + } + + SQLiteIndex SQLiteIndex::Open(const std::string& filePath, OpenDisposition disposition) + { + switch (disposition) + { + case AppInstaller::Repository::Microsoft::SQLiteIndex::OpenDisposition::Read: + return { filePath, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::None }; + case AppInstaller::Repository::Microsoft::SQLiteIndex::OpenDisposition::ReadWrite: + return { filePath, SQLite::Connection::OpenDisposition::ReadWrite, SQLite::Connection::OpenFlags::None }; + case AppInstaller::Repository::Microsoft::SQLiteIndex::OpenDisposition::Immutable: + { + // Following the algorithm set forth at https://sqlite.org/uri.html [3.1] to convert to a URI path + // The execution order builds out the string so that it shouldn't require any moves (other than growing) + std::string target; + // Add an 'arbitrary' growth size to prevent the majority of needing to grow (adding 'file:/' and '?immutable=1') + target.reserve(filePath.size() + 20); + + target += "file:"; + + bool wasLastCharSlash = false; + + if (filePath.size() >= 2 && filePath[1] == ':' && + ((filePath[0] >= 'a' && filePath[0] <= 'z') || + (filePath[0] >= 'A' && filePath[0] <= 'Z'))) + { + target += '/'; + wasLastCharSlash = true; + } + + for (char c : filePath) + { + bool wasThisCharSlash = false; + switch (c) + { + case '?': target += "%3f"; break; + case '#': target += "%23"; break; + case '\\': + case '/': + { + wasThisCharSlash = true; + if (!wasLastCharSlash) + { + target += '/'; + } + break; + } + default: target += c; break; + } + + wasLastCharSlash = wasThisCharSlash; + } + + target += "?immutable=1"; + + return { target, SQLite::Connection::OpenDisposition::ReadOnly, SQLite::Connection::OpenFlags::Uri }; + } + default: + THROW_HR(E_UNEXPECTED); + } + } + + SQLiteIndex::SQLiteIndex(const std::string& target, SQLite::Connection::OpenDisposition disposition, SQLite::Connection::OpenFlags flags) : + _dbconn(SQLite::Connection::Create(target, disposition, flags)) + { + _version = Schema::Version::GetSchemaVersion(_dbconn); + _interface = _version.CreateISQLiteIndex(); + } + + SQLiteIndex::SQLiteIndex(const std::string& target, Schema::Version version) : + _dbconn(SQLite::Connection::Create(target, SQLite::Connection::OpenDisposition::Create)) + { + _interface = version.CreateISQLiteIndex(); + _version = _interface->GetVersion(); + } +} diff --git a/src/RepositoryLib/Microsoft/SQLiteIndex.h b/src/RepositoryLib/Microsoft/SQLiteIndex.h @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/Version.h" + +#include <limits> +#include <memory> +#include <string> + +namespace AppInstaller::Repository::Microsoft +{ + // Holds the connection to the database, as well as the appropriate functionality to interface with it. + struct SQLiteIndex + { + SQLiteIndex(const SQLiteIndex&) = delete; + SQLiteIndex& operator=(const SQLiteIndex&) = delete; + + SQLiteIndex(SQLiteIndex&&) = default; + SQLiteIndex& operator=(SQLiteIndex&&) = default; + + // Creates a new index database of the given version. + static SQLiteIndex CreateNew(const std::string& filePath, Schema::Version version); + + // The disposition for opening the index. + enum class OpenDisposition + { + // Open for read only. + Read, + // Open for read and write. + ReadWrite, + // The database will not change while in use; open for immutable read. + Immutable, + }; + + // Opens an existing index database. + static SQLiteIndex Open(const std::string& filePath, OpenDisposition disposition); + + // Gets the schema version of the index. + Schema::Version GetVersion() const { return _version; } + + private: + // Constructor used to open an existing index. + SQLiteIndex(const std::string& target, SQLite::Connection::OpenDisposition disposition, SQLite::Connection::OpenFlags flags); + + // Constructor used to create a new index. + SQLiteIndex(const std::string& target, Schema::Version version); + + SQLite::Connection _dbconn; + Schema::Version _version; + std::unique_ptr<Schema::ISQLiteIndex> _interface; + }; +} diff --git a/src/RepositoryLib/Microsoft/Schema/1_0/Interface.cpp b/src/RepositoryLib/Microsoft/Schema/1_0/Interface.cpp @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Microsoft/Schema/1_0/Interface.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + Schema::Version Interface::GetVersion() const + { + return { 1, 0 }; + } +} diff --git a/src/RepositoryLib/Microsoft/Schema/1_0/Interface.h b/src/RepositoryLib/Microsoft/Schema/1_0/Interface.h @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Microsoft/Schema/ISQLiteIndex.h" + + +namespace AppInstaller::Repository::Microsoft::Schema::V1_0 +{ + // Interface to this schema version exposed through ISQLiteIndex. + struct Interface : public SQLiteIndexBase + { + Schema::Version GetVersion() const override; + }; +} diff --git a/src/RepositoryLib/Microsoft/Schema/ISQLiteIndex.h b/src/RepositoryLib/Microsoft/Schema/ISQLiteIndex.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/Version.h" + + +namespace AppInstaller::Repository::Microsoft::Schema +{ + // Forward declarations + struct Version; + + // The common interface used to interact with all schema versions of the index. + struct ISQLiteIndex + { + virtual ~ISQLiteIndex() = default; + + // Version 1.0 + + // Gets the schema version that this index interface is built for. + virtual Schema::Version GetVersion() const = 0; + }; + + + // Common base class used by all schema versions. + struct SQLiteIndexBase : public ISQLiteIndex + { + }; +} diff --git a/src/RepositoryLib/Microsoft/Schema/MetadataTable.cpp b/src/RepositoryLib/Microsoft/Schema/MetadataTable.cpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "MetadataTable.h" + + +namespace AppInstaller::Repository::Microsoft::Schema +{ + char const* const s_MetadataValueName_MajorVersion = "majorVersion"; + char const* const s_MetadataValueName_MinorVersion = "minorVersion"; + + // Table data [note that this table is not versioned, and thus *cannot change*] + static char const* const s_MetadataTable_Table_Name = "metadata"; + static char const* const s_MetadataTable_Column_Name = "name"; + static char const* const s_MetadataTable_Column_Value = "value"; + + static char const* const s_MetadataTable_Table_Create = R"( +CREATE TABLE [metadata]( + [name] TEXT PRIMARY KEY NOT NULL UNIQUE, + [value] TEXT NOT NULL) +)"; + + // Statements + static char const* const s_MetadataTableStmt_GetNamedValue = "select [value] from [metadata] where [name] = ?"; + static char const* const s_MetadataTableStmt_SetNamedValue = "insert or replace into [metadata] ([name], [value]) values (?, ?)"; + + void MetadataTable::Create(SQLite::Connection& connection) + { + SQLite::Statement create = SQLite::Statement::Create(connection, s_MetadataTable_Table_Create); + create.Step(); + } + + SQLite::Statement MetadataTable::GetNamedValueStatement(SQLite::Connection& connection, const std::string& name) + { + THROW_HR_IF(E_INVALIDARG, name.empty()); + SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_GetNamedValue); + result.Bind(1, name); + THROW_HR_IF(E_NOT_SET, !result.Step()); + return result; + } + + SQLite::Statement MetadataTable::SetNamedValueStatement(SQLite::Connection& connection, const std::string& name) + { + THROW_HR_IF(E_INVALIDARG, name.empty()); + SQLite::Statement result = SQLite::Statement::Create(connection, s_MetadataTableStmt_SetNamedValue); + result.Bind(1, name); + return result; + } +} diff --git a/src/RepositoryLib/Microsoft/Schema/MetadataTable.h b/src/RepositoryLib/Microsoft/Schema/MetadataTable.h @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" + +#include <wil/result_macros.h> + +namespace AppInstaller::Repository::Microsoft::Schema +{ + // Named metadata values are defined here to reduce the risk of duplicate names. + + // Version 1.0 + extern char const* const s_MetadataValueName_MajorVersion; + extern char const *const s_MetadataValueName_MinorVersion; + + // The metadata table for the index. + // Contains a fixed-schema set of named values that can be used to determine how to read the rest of the index. + struct MetadataTable + { + static void Create(SQLite::Connection& connection); + + // Gets the named value from the metadata table, interpreting it as the given type. + template <typename Value> + static Value GetNamedValue(SQLite::Connection& connection, const std::string& name) + { + SQLite::Statement statement = GetNamedValueStatement(connection, name); + return statement.GetColumn<Value>(0); + } + + // Sets the named value into the metadata table. + template <typename Value> + static void SetNamedValue(SQLite::Connection& connection, const std::string& name, Value&& v) + { + SQLite::Statement statement = SetNamedValueStatement(connection, name); + statement.Bind(2, std::forward<Value>(v)); + THROW_HR_IF(E_UNEXPECTED, statement.Step()); + } + + private: + // Internal function that gets the named value. + static SQLite::Statement GetNamedValueStatement(SQLite::Connection& connection, const std::string& name); + + // Internal function that sets the named value. + static SQLite::Statement SetNamedValueStatement(SQLite::Connection& connection, const std::string& name); + }; +} diff --git a/src/RepositoryLib/Microsoft/Schema/Version.cpp b/src/RepositoryLib/Microsoft/Schema/Version.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Version.h" +#include "MetadataTable.h" + +#include "1_0/Interface.h" + +namespace AppInstaller::Repository::Microsoft::Schema +{ + Version Version::GetSchemaVersion(SQLite::Connection& connection) + { + int major = MetadataTable::GetNamedValue<int>(connection, Schema::s_MetadataValueName_MajorVersion); + int minor = MetadataTable::GetNamedValue<int>(connection, Schema::s_MetadataValueName_MinorVersion); + + return { static_cast<uint32_t>(major), static_cast<uint32_t>(minor) }; + } + + void Version::SetSchemaVersion(SQLite::Connection& connection) + { + SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, "version_setschemaversion"); + + MetadataTable::SetNamedValue(connection, Schema::s_MetadataValueName_MajorVersion, static_cast<int>(MajorVersion)); + MetadataTable::SetNamedValue(connection, Schema::s_MetadataValueName_MinorVersion, static_cast<int>(MinorVersion)); + + savepoint.Commit(); + } + + // Creates the interface object for this version. + std::unique_ptr<ISQLiteIndex> Version::CreateISQLiteIndex() + { + if (*this == Version{ 1, 0 } || + this->MajorVersion == 1 || + this->IsLatest()) + { + return std::make_unique<V1_0::Interface>(); + } + + // We do not have the capacity to operate on this schema version + THROW_HR(E_NOT_SET); + } +} diff --git a/src/RepositoryLib/Microsoft/Schema/Version.h b/src/RepositoryLib/Microsoft/Schema/Version.h @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "SQLiteWrapper.h" +#include "Microsoft/Schema/ISQLiteIndex.h" + +#include <limits> +#include <memory> + +namespace AppInstaller::Repository::Microsoft::Schema +{ + // Forward declarations + struct ISQLiteIndex; + + // Represents the schema version of the index. + struct Version + { + // The major version of the schema. + // All minor changes to this major version must be backward compatible. + uint32_t MajorVersion{}; + // The minor version of the schema. + // All changes to the schema warrant a change to the minor version. + uint32_t MinorVersion{}; + + bool operator==(Version other) const + { + return (MajorVersion == other.MajorVersion && MinorVersion == other.MinorVersion); + } + + // Gets a version that represents the latest schema known to the implementation. + static constexpr Version Latest() { return { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() }; } + + // Gets a version that represents the latest schema known to the implementation for the given major version. + static constexpr Version LatestForMajor(uint32_t majorVersion) { return { majorVersion, std::numeric_limits<uint32_t>::max() }; } + + // Determines if this version represents the latest schema. + bool IsLatest() const { return (MajorVersion == std::numeric_limits<uint32_t>::max() && MinorVersion == std::numeric_limits<uint32_t>::max()); } + + // Determines if this version represents the latest schema of the given major version. + bool IsLatestForMajor(uint32_t majorVersion) const { return (MajorVersion == majorVersion && MinorVersion == std::numeric_limits<uint32_t>::max()); } + + // Determines the schema version of the opened index. + static Version GetSchemaVersion(SQLite::Connection& connection); + + // Writes the current version to the given index. + void SetSchemaVersion(SQLite::Connection& connection); + + // Creates the interface object for this version. + std::unique_ptr<ISQLiteIndex> CreateISQLiteIndex(); + }; +} diff --git a/src/RepositoryLib/RepositoryLib.vcxproj b/src/RepositoryLib/RepositoryLib.vcxproj @@ -166,16 +166,26 @@ </Link> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="Microsoft\Schema\1_0\Interface.h" /> + <ClInclude Include="Microsoft\Schema\ISQLiteIndex.h" /> + <ClInclude Include="Microsoft\Schema\MetadataTable.h" /> + <ClInclude Include="Microsoft\Schema\Version.h" /> + <ClInclude Include="Microsoft\SQLiteIndex.h" /> <ClInclude Include="pch.h" /> <ClInclude Include="SQLiteWrapper.h" /> </ItemGroup> <ItemGroup> + <ClCompile Include="Microsoft\Schema\1_0\Interface.cpp" /> + <ClCompile Include="Microsoft\Schema\MetadataTable.cpp" /> + <ClCompile Include="Microsoft\Schema\Version.cpp" /> + <ClCompile Include="Microsoft\SQLiteIndex.cpp" /> <ClCompile Include="pch.cpp"> <PrecompiledHeader>Create</PrecompiledHeader> </ClCompile> <ClCompile Include="SQLiteWrapper.cpp" /> </ItemGroup> <ItemGroup> + <None Include="Microsoft\README.md" /> <None Include="packages.config" /> <None Include="PropertySheet.props" /> </ItemGroup> diff --git a/src/RepositoryLib/RepositoryLib.vcxproj.filters b/src/RepositoryLib/RepositoryLib.vcxproj.filters @@ -13,6 +13,15 @@ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> </Filter> + <Filter Include="Microsoft"> + <UniqueIdentifier>{2d97761a-4967-4ece-b5fd-d96f346731ed}</UniqueIdentifier> + </Filter> + <Filter Include="Microsoft\Schema"> + <UniqueIdentifier>{7996cf09-4e36-4009-b48d-53ffc3bfa956}</UniqueIdentifier> + </Filter> + <Filter Include="Microsoft\Schema\1_0"> + <UniqueIdentifier>{cff561cd-211b-4cab-87f5-39359eef1e8d}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h"> @@ -21,6 +30,21 @@ <ClInclude Include="SQLiteWrapper.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="Microsoft\SQLiteIndex.h"> + <Filter>Microsoft</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\MetadataTable.h"> + <Filter>Microsoft\Schema</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\Version.h"> + <Filter>Microsoft\Schema</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\ISQLiteIndex.h"> + <Filter>Microsoft\Schema</Filter> + </ClInclude> + <ClInclude Include="Microsoft\Schema\1_0\Interface.h"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -29,9 +53,24 @@ <ClCompile Include="SQLiteWrapper.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="Microsoft\SQLiteIndex.cpp"> + <Filter>Microsoft</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\MetadataTable.cpp"> + <Filter>Microsoft\Schema</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\Version.cpp"> + <Filter>Microsoft\Schema</Filter> + </ClCompile> + <ClCompile Include="Microsoft\Schema\1_0\Interface.cpp"> + <Filter>Microsoft\Schema\1_0</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> <None Include="packages.config" /> + <None Include="Microsoft\README.md"> + <Filter>Microsoft</Filter> + </None> </ItemGroup> </Project> \ No newline at end of file diff --git a/src/RepositoryLib/SQLiteWrapper.cpp b/src/RepositoryLib/SQLiteWrapper.cpp @@ -3,6 +3,8 @@ #include "pch.h" #include "SQLiteWrapper.h" +#include <wil/result_macros.h> + #define THROW_SQLITE(_error_) throw SQLiteException(_error_) #define THROW_IF_SQLITE_FAILED(_statement_) \ @@ -90,7 +92,7 @@ namespace AppInstaller::Repository::SQLite sqlite3_finalize(_stmt); } - bool Statement::Step() + bool Statement::Step(bool failFastOnError) { int result = sqlite3_step(_stmt); @@ -107,7 +109,14 @@ namespace AppInstaller::Repository::SQLite else { _state = State::Error; - THROW_SQLITE(result); + if (failFastOnError) + { + FAIL_FAST_MSG("Critical SQL statement failed"); + } + else + { + THROW_SQLITE(result); + } } } @@ -117,4 +126,44 @@ namespace AppInstaller::Repository::SQLite sqlite3_reset(_stmt); _state = State::Prepared; } + + Savepoint::Savepoint(Connection& connection, std::string&& name) : + _name(std::move(name)) + { + using namespace std::string_literals; + + Statement begin = Statement::Create(connection, "SAVEPOINT ["s + name + "]"); + _rollback = Statement::Create(connection, "ROLLBACK TO ["s + name + "]", true); + _commit = Statement::Create(connection, "RELEASE ["s + name + "]", true); + + begin.Step(); + } + + Savepoint Savepoint::Create(Connection& connection, std::string&& name) + { + return { connection, std::move(name) }; + } + + Savepoint::~Savepoint() + { + Rollback(); + } + + void Savepoint::Rollback() + { + if (_inProgress) + { + _rollback.Step(true); + _inProgress = false; + } + } + + void Savepoint::Commit() + { + if (_inProgress) + { + _commit.Step(true); + _inProgress = false; + } + } } diff --git a/src/RepositoryLib/SQLiteWrapper.h b/src/RepositoryLib/SQLiteWrapper.h @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once +#include <wil/result_macros.h> #include <winsqlite/winsqlite3.h> + #include <string> #include <stdexcept> #include <system_error> @@ -18,11 +20,11 @@ namespace AppInstaller::Repository::SQLite { static void Bind(sqlite3_stmt*, int, T&&) { - static_assert(false, "No type specific override has been supplied."); + static_assert(false, "No type specific override has been supplied"); } static T GetColumn(sqlite3_stmt*, int) { - static_assert(false, "No type specific override has been supplied."); + static_assert(false, "No type specific override has been supplied"); } }; @@ -52,31 +54,33 @@ namespace AppInstaller::Repository::SQLite static const std::error_category& GetCategory() noexcept; }; - // The disposition for opening a database connection. - enum class OpenDisposition : int - { - // Open existing database for reading. - ReadOnly = SQLITE_OPEN_READONLY, - // Open existing database for reading and writing. - ReadWrite = SQLITE_OPEN_READWRITE, - // Create new database for reading and writing. - Create = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - }; - - // Flags for opening a database connection. - enum class OpenFlags : int - { - // No flags specified. - None = 0, - // Indicate that the target can be a URI. - Uri = SQLITE_OPEN_URI, - }; - // The connection to a database. struct Connection { + // The disposition for opening a database connection. + enum class OpenDisposition : int + { + // Open existing database for reading. + ReadOnly = SQLITE_OPEN_READONLY, + // Open existing database for reading and writing. + ReadWrite = SQLITE_OPEN_READWRITE, + // Create new database for reading and writing. + Create = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + }; + + // Flags for opening a database connection. + enum class OpenFlags : int + { + // No flags specified. + None = 0, + // Indicate that the target can be a URI. + Uri = SQLITE_OPEN_URI, + }; + static Connection Create(const std::string& target, OpenDisposition disposition, OpenFlags flags = OpenFlags::None); + Connection() = default; + Connection(const Connection&) = delete; Connection& operator=(const Connection&) = delete; @@ -98,11 +102,13 @@ namespace AppInstaller::Repository::SQLite { static Statement Create(Connection& connection, const std::string& sql, bool persistent = false); + Statement() = default; + Statement(const Statement&) = delete; Statement& operator=(const Statement&) = delete; - Statement(Statement&& other) noexcept { std::swap(_stmt, other._stmt); } - Statement& operator=(Statement&& other) noexcept { std::swap(_stmt, other._stmt); return *this; } + Statement(Statement&& other) noexcept { std::swap(_stmt, other._stmt); std::swap(_state, other._state); } + Statement& operator=(Statement&& other) noexcept { std::swap(_stmt, other._stmt); std::swap(_state, other._state); return *this; } ~Statement(); @@ -135,17 +141,14 @@ namespace AppInstaller::Repository::SQLite // Evaluate the statement; either retrieving the next row or executing some action. // Returns true if there is a row of data, or false if there is none. // This return value is the equivalent of 'GetState() == State::HasRow' after calling Step. - bool Step(); + bool Step(bool failFastOnError = false); // Gets the value of the specified column from the current row. // The index is 0 based. template <typename Value> Value GetColumn(int column) { - if (_state != State::HasRow) - { - throw std::out_of_range("SQLite statement does not have a row available."); - } + THROW_HR_IF(E_BOUNDS, _state != State::HasRow); return details::ParameterSpecifics<Value>::GetColumn(_stmt, column); } @@ -171,14 +174,40 @@ namespace AppInstaller::Repository::SQLite template <typename... Values, int... I> std::tuple<Values...> GetRowImpl(std::integer_sequence<int, I...>) { - if (_state != State::HasRow) - { - throw std::out_of_range("SQLite statement does not have a row available."); - } + THROW_HR_IF(E_BOUNDS, _state != State::HasRow); return std::make_tuple(details::ParameterSpecifics<Values>::GetColumn(_stmt, I)...); } sqlite3_stmt* _stmt = nullptr; State _state = State::Prepared; }; + + // A SQLite savepoint. + struct Savepoint + { + // Creates a savepoint, beginning it. + static Savepoint Create(Connection& connection, std::string&& name); + + Savepoint(const Savepoint&) = delete; + Savepoint& operator=(const Savepoint&) = delete; + + Savepoint(Savepoint&&) = default; + Savepoint& operator=(Savepoint&&) = default; + + ~Savepoint(); + + // Rolls back the Savepoint. + void Rollback(); + + // Commits the Savepoint. + void Commit(); + + private: + Savepoint(Connection& connection, std::string&& name); + + std::string _name; + bool _inProgress = true; + Statement _rollback; + Statement _commit; + }; } diff --git a/src/RepositoryLib/pch.h b/src/RepositoryLib/pch.h @@ -2,10 +2,19 @@ // Licensed under the MIT License. #pragma once +#define NOMINMAX #include <windows.h> #include "TelemetryWrapper.h" +#include <wil/result_macros.h> + #include <winsqlite/winsqlite3.h> #include <winrt/Windows.Foundation.h> +#include <string> +#include <stdexcept> +#include <system_error> +#include <tuple> +#include <type_traits> +#include <utility> diff --git a/src/RepositoryLibTests/RepositoryLibTests.vcxproj b/src/RepositoryLibTests/RepositoryLibTests.vcxproj @@ -1,161 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props')" /> - <PropertyGroup Label="Globals"> - <CppWinRTOptimized>true</CppWinRTOptimized> - <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge> - <CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata> - <MinimalCoreWin>true</MinimalCoreWin> - <VCProjectVersion>15.0</VCProjectVersion> - <ProjectGuid>{89b1aab4-2bbc-4b65-9ed7-a01d5cf88230}</ProjectGuid> - <Keyword>Win32Proj</Keyword> - <RootNamespace>RepositoryLibTests</RootNamespace> - <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion> - <WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Debug|x64"> - <Configuration>Debug</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|x64"> - <Configuration>Release</Configuration> - <Platform>x64</Platform> - </ProjectConfiguration> - </ItemGroup> - <PropertyGroup Label="Configuration"> - <ConfigurationType>Application</ConfigurationType> - <PlatformToolset>v140</PlatformToolset> - <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset> - <PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> - <UseDebugLibraries>true</UseDebugLibraries> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> - <ImportGroup Label="ExtensionSettings"> - </ImportGroup> - <ImportGroup Label="Shared"> - <Import Project="..\catch2\catch2.vcxitems" Label="Shared" /> - </ImportGroup> - <ImportGroup Label="PropertySheets"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets"> - <Import Project="PropertySheet.props" /> - </ImportGroup> - <PropertyGroup Label="UserMacros" /> - <ItemDefinitionGroup> - <ClCompile> - <PrecompiledHeader>Use</PrecompiledHeader> - <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <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> - </ClCompile> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> - <ClCompile> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> - <ClCompile> - <PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> - <ClCompile> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> - <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(MSBuildThisFileDirectory)..\RepositoryLib;%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)..\PackageManifestHelper;$(MSBuildThisFileDirectory)..\YamlCppLib\yaml-cpp\include</AdditionalIncludeDirectories> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateWindowsMetadata>false</GenerateWindowsMetadata> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> - <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">winsqlite3.lib;%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemGroup> - <ClInclude Include="pch.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="PackageManifestHelper.cpp" /> - <ClCompile Include="main.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> - </ClCompile> - <ClCompile Include="pch.cpp"> - <PrecompiledHeader>Create</PrecompiledHeader> - </ClCompile> - <ClCompile Include="SQLiteWrapper.cpp" /> - </ItemGroup> - <ItemGroup> - <CopyFileToFolders Include="TestData\GoodManifest.yml"> - <DeploymentContent>true</DeploymentContent> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> - <DeploymentContent>true</DeploymentContent> - </CopyFileToFolders> - <None Include="packages.config" /> - <None Include="PropertySheet.props" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\PackageManifestHelper\PackageManifestHelper.vcxproj"> - <Project>{f8f75341-9455-4d5b-824a-47a7818ee864}</Project> - </ProjectReference> - <ProjectReference Include="..\RepositoryLib\RepositoryLib.vcxproj"> - <Project>{5eb88068-5fb9-4e69-89b2-72dbc5e068f9}</Project> - </ProjectReference> - <ProjectReference Include="..\YamlCppLib\YamlCppLib.vcxproj"> - <Project>{8bb94bb8-374f-4294-bca1-c7811514a6b7}</Project> - </ProjectReference> - </ItemGroup> - <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> - <ImportGroup Label="ExtensionTargets"> - <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets')" /> - </ImportGroup> - <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> - <PropertyGroup> - <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> - </PropertyGroup> - <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.props'))" /> - <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.191202.6\build\native\Microsoft.Windows.CppWinRT.targets'))" /> - </Target> -</Project>- \ No newline at end of file diff --git a/src/RepositoryLibTests/RepositoryLibTests.vcxproj.filters b/src/RepositoryLibTests/RepositoryLibTests.vcxproj.filters @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - <Filter Include="TestData"> - <UniqueIdentifier>{d5cac203-3846-4b39-a1cd-8de9303757b4}</UniqueIdentifier> - </Filter> - </ItemGroup> - <ItemGroup> - <ClInclude Include="pch.h"> - <Filter>Header Files</Filter> - </ClInclude> - </ItemGroup> - <ItemGroup> - <ClCompile Include="pch.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="main.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="SQLiteWrapper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - <ClCompile Include="PackageManifestHelper.cpp"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> - <ItemGroup> - <None Include="PropertySheet.props" /> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <CopyFileToFolders Include="TestData\BadManifest-MissingName.yml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - <CopyFileToFolders Include="TestData\GoodManifest.yml"> - <Filter>TestData</Filter> - </CopyFileToFolders> - </ItemGroup> -</Project>- \ No newline at end of file diff --git a/src/RepositoryLibTests/SQLiteWrapper.cpp b/src/RepositoryLibTests/SQLiteWrapper.cpp @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include <SQLiteWrapper.h> - -using namespace AppInstaller::Repository::SQLite; - -#define SQLITE_MEMORY_DB_CONNECTION_TARGET ":memory:" - -static const char* s_CreateSimpleTestTableSQL = R"( -CREATE TABLE [main].[simpletest]( - [first] INT, - [second] TEXT); -)"; - -static const char* s_insertToSimpleTestTableSQL = R"( -insert into simpletest (first, second) values (?, ?) -)"; - -static const char* s_selectFromSimpleTestTableSQL = R"( -select first, second from simpletest -)"; - -void CreateSimpleTestTable(Connection& connection) -{ - Statement createTable = Statement::Create(connection, s_CreateSimpleTestTableSQL); - REQUIRE_FALSE(createTable.Step()); - REQUIRE(createTable.GetState() == Statement::State::Completed); -} - -void InsertIntoSimpleTestTable(Connection& connection, int firstVal, const std::string& secondVal) -{ - Statement insert = Statement::Create(connection, s_insertToSimpleTestTableSQL); - - insert.Bind(1, firstVal); - insert.Bind(2, secondVal); - - REQUIRE_FALSE(insert.Step()); - REQUIRE(insert.GetState() == Statement::State::Completed); -} - -void SelectFromSimpleTestTableOnlyOneRow(Connection& connection, int firstVal, const std::string& secondVal) -{ - Statement select = Statement::Create(connection, s_selectFromSimpleTestTableSQL); - REQUIRE(select.Step()); - REQUIRE(select.GetState() == Statement::State::HasRow); - - int firstRead = select.GetColumn<int>(0); - std::string secondRead = select.GetColumn<std::string>(1); - - REQUIRE(firstVal == firstRead); - REQUIRE(secondVal == secondRead); - - auto tuple = select.GetRow<int, std::string>(); - - REQUIRE(firstVal == std::get<0>(tuple)); - REQUIRE(secondVal == std::get<1>(tuple)); - - REQUIRE_FALSE(select.Step()); - REQUIRE(select.GetState() == Statement::State::Completed); - - select.Reset(); - REQUIRE(select.GetState() == Statement::State::Prepared); - - REQUIRE(select.Step()); - REQUIRE(select.GetState() == Statement::State::HasRow); -} - -TEST_CASE("SQLiteWrapperMemoryCreate", "[sqlitewrapper]") -{ - Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, OpenDisposition::Create); - - CreateSimpleTestTable(connection); - - int firstVal = 1; - std::string secondVal = "test"; - - InsertIntoSimpleTestTable(connection, firstVal, secondVal); - - SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); -} - -TEST_CASE("SQLiteWrapperFileCreateAndReopen", "[sqlitewrapper]") -{ - char tempPath[MAX_PATH]{}; - REQUIRE(GetTempPathA(MAX_PATH, tempPath) != 0); - - srand(static_cast<unsigned int>(time(NULL))); - std::stringstream tempFileName; - tempFileName << tempPath << "\\repolibtest_tempdb" << rand() << ".db"; - - INFO("Using temporary file named: " << tempFileName.str()); - - DeleteFileA(tempFileName.str().c_str()); - - int firstVal = 1; - std::string secondVal = "test"; - - // Create the DB and some data - { - Connection connection = Connection::Create(tempFileName.str(), OpenDisposition::Create); - - CreateSimpleTestTable(connection); - - InsertIntoSimpleTestTable(connection, firstVal, secondVal); - } - - // Reopen the DB and read data - { - Connection connection = Connection::Create(tempFileName.str(), OpenDisposition::ReadWrite); - - SelectFromSimpleTestTableOnlyOneRow(connection, firstVal, secondVal); - } -} diff --git a/src/RepositoryLibTests/main.cpp b/src/RepositoryLibTests/main.cpp @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#define CATCH_CONFIG_RUNNER -#include <catch.hpp> -#include <winrt/Windows.Foundation.h> - -using namespace winrt; -using namespace Windows::Foundation; - -int main(int argc, char** argv) -{ - init_apartment(); - return Catch::Session().run(argc, argv); -} diff --git a/src/RepositoryLibTests/packages.config b/src/RepositoryLibTests/packages.config @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="Microsoft.Windows.CppWinRT" version="2.0.191202.6" targetFramework="native" /> -</packages>- \ No newline at end of file diff --git a/src/RepositoryLibTests/pch.h b/src/RepositoryLibTests/pch.h @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <Windows.h> - -#include <catch.hpp> - -#include <winrt/Windows.Foundation.h> -#include <winrt/Windows.Foundation.Collections.h> - -#include <iostream> -#include <sstream> -#include <utility> - -#include <yaml-cpp/yaml.h>- \ No newline at end of file diff --git a/src/Telemetry/TelemetryWrapper.cpp b/src/Telemetry/TelemetryWrapper.cpp @@ -30,7 +30,7 @@ namespace AppInstaller::CLI { TraceLoggingWrite(g_hTelemetryProvider, PKGMGR_CLIENT_EVENT_INFO, - TraceLoggingCountedWideString(message.data(), message.size(), PKGMGR_CLIENT_MESSAGE), + TraceLoggingCountedWideString(message.data(), static_cast<ULONG>(message.size()), PKGMGR_CLIENT_MESSAGE), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); } diff --git a/src/YamlCppLib/YamlCppLib.vcxproj b/src/YamlCppLib/YamlCppLib.vcxproj @@ -63,6 +63,38 @@ <WholeProgramOptimization>true</WholeProgramOptimization> <LinkIncremental>false</LinkIncremental> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)x86\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</OutDir> + </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> <ClCompile> <AdditionalIncludeDirectories>yaml-cpp\include</AdditionalIncludeDirectories>