From af10c45d7b3a44af0b461a557847fe03263dcc10 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 17:06:54 -0700 Subject: Move burn into burn --- src/burn/test/BurnUnitTest/AssemblyInfo.cpp | 12 + src/burn/test/BurnUnitTest/BurnTestException.h | 93 ++ src/burn/test/BurnUnitTest/BurnTestFixture.h | 75 + src/burn/test/BurnUnitTest/BurnUnitTest.h | 48 + src/burn/test/BurnUnitTest/BurnUnitTest.rc | 6 + src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj | 109 ++ .../test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 80 ++ src/burn/test/BurnUnitTest/CacheTest.cpp | 119 ++ src/burn/test/BurnUnitTest/ElevationTest.cpp | 221 +++ src/burn/test/BurnUnitTest/ManifestHelpers.cpp | 41 + src/burn/test/BurnUnitTest/ManifestHelpers.h | 24 + src/burn/test/BurnUnitTest/ManifestTest.cpp | 62 + src/burn/test/BurnUnitTest/PlanTest.cpp | 1473 ++++++++++++++++++++ src/burn/test/BurnUnitTest/RegistrationTest.cpp | 772 ++++++++++ src/burn/test/BurnUnitTest/SearchTest.cpp | 815 +++++++++++ .../TestData/CacheTest/CacheSignatureTest.File | 1 + .../BasicFunctionality_BundleA_manifest.xml | 1 + .../PlanTest/MsiTransaction_BundleAv1_manifest.xml | 1 + .../PlanTest/Slipstream_BundleA_manifest.xml | 1 + src/burn/test/BurnUnitTest/VariableHelpers.cpp | 217 +++ src/burn/test/BurnUnitTest/VariableHelpers.h | 36 + src/burn/test/BurnUnitTest/VariableTest.cpp | 532 +++++++ src/burn/test/BurnUnitTest/VariantTest.cpp | 221 +++ src/burn/test/BurnUnitTest/packages.config | 15 + src/burn/test/BurnUnitTest/precomp.cpp | 3 + src/burn/test/BurnUnitTest/precomp.h | 79 ++ 26 files changed, 5057 insertions(+) create mode 100644 src/burn/test/BurnUnitTest/AssemblyInfo.cpp create mode 100644 src/burn/test/BurnUnitTest/BurnTestException.h create mode 100644 src/burn/test/BurnUnitTest/BurnTestFixture.h create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.h create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.rc create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj create mode 100644 src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters create mode 100644 src/burn/test/BurnUnitTest/CacheTest.cpp create mode 100644 src/burn/test/BurnUnitTest/ElevationTest.cpp create mode 100644 src/burn/test/BurnUnitTest/ManifestHelpers.cpp create mode 100644 src/burn/test/BurnUnitTest/ManifestHelpers.h create mode 100644 src/burn/test/BurnUnitTest/ManifestTest.cpp create mode 100644 src/burn/test/BurnUnitTest/PlanTest.cpp create mode 100644 src/burn/test/BurnUnitTest/RegistrationTest.cpp create mode 100644 src/burn/test/BurnUnitTest/SearchTest.cpp create mode 100644 src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml create mode 100644 src/burn/test/BurnUnitTest/VariableHelpers.cpp create mode 100644 src/burn/test/BurnUnitTest/VariableHelpers.h create mode 100644 src/burn/test/BurnUnitTest/VariableTest.cpp create mode 100644 src/burn/test/BurnUnitTest/VariantTest.cpp create mode 100644 src/burn/test/BurnUnitTest/packages.config create mode 100644 src/burn/test/BurnUnitTest/precomp.cpp create mode 100644 src/burn/test/BurnUnitTest/precomp.h (limited to 'src/burn/test/BurnUnitTest') diff --git a/src/burn/test/BurnUnitTest/AssemblyInfo.cpp b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..0282b1b7 --- /dev/null +++ b/src/burn/test/BurnUnitTest/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; + +[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")]; +[assembly: AssemblyDescriptionAttribute("Burn unit tests")]; +[assembly: AssemblyCultureAttribute("")]; +[assembly: ComVisible(false)]; diff --git a/src/burn/test/BurnUnitTest/BurnTestException.h b/src/burn/test/BurnUnitTest/BurnTestException.h new file mode 100644 index 00000000..bd94b4fc --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnTestException.h @@ -0,0 +1,93 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + + public ref struct BurnTestException : public System::Exception + { + public: + BurnTestException(HRESULT error) + { + this->HResult = error; + } + + BurnTestException(HRESULT error, String^ message) + : Exception(message) + { + this->HResult = error; + } + + property Int32 ErrorCode + { + Int32 get() + { + return this->HResult; + } + } + + }; +} +} +} +} +} + +// this class is used by __TestThrowOnFailure_Format() below to deallocate +// the string created after the function call has returned +class __TestThrowOnFailure_StringFree +{ + LPWSTR m_scz; + +public: + __TestThrowOnFailure_StringFree(LPWSTR scz) + { + m_scz = scz; + } + + ~__TestThrowOnFailure_StringFree() + { + ReleaseStr(m_scz); + } + + operator LPCWSTR() + { + return m_scz; + } +}; + +// used by the TestThrowOnFailure macros to format the error string and +// return an LPCWSTR that can be used to initialize a System::String +#pragma warning (push) +#pragma warning (disable : 4793) +inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...) +{ + Assert(wzFormat && *wzFormat); + + HRESULT hr = S_OK; + LPWSTR scz = NULL; + va_list args; + + va_start(args, wzFormat); + hr = StrAllocFormattedArgs(&scz, wzFormat, args); + va_end(args); + ExitOnFailure(hr, "Failed to format message string."); + +LExit: + return scz; +} +#pragma warning (pop) + +#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); } +#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); } +#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); } diff --git a/src/burn/test/BurnUnitTest/BurnTestFixture.h b/src/burn/test/BurnUnitTest/BurnTestFixture.h new file mode 100644 index 00000000..103972ef --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnTestFixture.h @@ -0,0 +1,75 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace WixBuildTools::TestSupport; + + public ref class BurnTestFixture : IDisposable + { + public: + BurnTestFixture() + { + HRESULT hr = XmlInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize XML support."); + + hr = RegInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Regutil."); + + hr = CrypInitialize(); + TestThrowOnFailure(hr, L"Failed to initialize Cryputil."); + + PlatformInitialize(); + + this->testDirectory = WixBuildTools::TestSupport::TestData::Get(); + + LogInitialize(::GetModuleHandleW(NULL)); + + LogSetLevel(REPORT_DEBUG, FALSE); + + hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL); + TestThrowOnFailure(hr, L"Failed to open log."); + } + + ~BurnTestFixture() + { + CrypUninitialize(); + XmlUninitialize(); + RegUninitialize(); + LogUninitialize(FALSE); + } + + property String^ DataDirectory + { + String^ get() + { + return this->testDirectory; + } + } + + property String^ TestDirectory + { + String^ get() + { + return this->testDirectory; + } + } + + private: + String^ testDirectory; + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.h b/src/burn/test/BurnUnitTest/BurnUnitTest.h new file mode 100644 index 00000000..ed1d2956 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.h @@ -0,0 +1,48 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + [CollectionDefinition("Burn")] + public ref class BurnCollectionDefinition : ICollectionFixture + { + + }; + + [Collection("Burn")] + public ref class BurnUnitTest + { + public: + BurnUnitTest(BurnTestFixture^ fixture) + { + this->testContext = fixture; + } + + property BurnTestFixture^ TestContext + { + BurnTestFixture^ get() + { + return this->testContext; + } + } + + private: + BurnTestFixture^ testContext; + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.rc b/src/burn/test/BurnUnitTest/BurnUnitTest.rc new file mode 100644 index 00000000..3a815db2 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.rc @@ -0,0 +1,6 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#define VER_APP +#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll" +#define VER_INTERNAL_NAME "setup" +#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj new file mode 100644 index 00000000..33c8ed6c --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -0,0 +1,109 @@ + + + + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + + + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} + {9D1F1BA3-9393-4833-87A3-D5F1FC08EF67} + UnitTest + ManagedCProj + DynamicLibrary + Unicode + true + false + + + + + + + $(ProjectDir)..\..\..\..\balutil\src\WixToolset.BootstrapperCore.Native\inc + $(ProjectAdditionalIncludeDirectories);..\..\engine + cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib + + + + + + + + + + + Create + + 4564;4691 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\packages\WixBuildTools.TestSupport.4.0.50\lib\net472\WixBuildTools.TestSupport.dll + + + ..\..\..\packages\WixBuildTools.TestSupport.Native.4.0.50\lib\net472\WixBuildTools.TestSupport.Native.dll + + + + + {8119537D-E1D9-6591-D51A-49770A2F9C37} + + + + + + + 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}. + + + + + + + diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters new file mode 100644 index 00000000..f9461f53 --- /dev/null +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -0,0 +1,80 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/CacheTest.cpp b/src/burn/test/BurnUnitTest/CacheTest.cpp new file mode 100644 index 00000000..d0cc237f --- /dev/null +++ b/src/burn/test/BurnUnitTest/CacheTest.cpp @@ -0,0 +1,119 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* pMessage, + __in LPVOID pvContext + ); + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER TotalFileSize, + __in LARGE_INTEGER TotalBytesTransferred, + __in LARGE_INTEGER StreamSize, + __in LARGE_INTEGER StreamBytesTransferred, + __in DWORD dwStreamNumber, + __in DWORD dwCallbackReason, + __in HANDLE hSourceFile, + __in HANDLE hDestinationFile, + __in_opt LPVOID lpData + ); + +typedef struct _CACHE_TEST_CONTEXT +{ +} CACHE_TEST_CONTEXT; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace Xunit; + + public ref class CacheTest : BurnUnitTest + { + public: + CacheTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void CacheSignatureTest() + { + HRESULT hr = S_OK; + BURN_PACKAGE package = { }; + BURN_PAYLOAD payload = { }; + LPWSTR sczPayloadPath = NULL; + BYTE* pb = NULL; + DWORD cb = NULL; + CACHE_TEST_CONTEXT context = { }; + + try + { + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\CacheTest\\CacheSignatureTest.File", &sczPayloadPath); + Assert::True(S_OK == hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist."); + + hr = StrAllocHexDecode(L"25e61cd83485062b70713aebddd3fe4992826cb121466fddc8de3eacb1e42f39d4bdd8455d95eec8c9529ced4c0296ab861931fe2c86df2f2b4e8d259a6d9223", &pb, &cb); + Assert::Equal(S_OK, hr); + + package.fPerMachine = FALSE; + package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest"; + payload.sczKey = L"CacheSignatureTest.PayloadKey"; + payload.sczFilePath = L"CacheSignatureTest.File"; + payload.pbHash = pb; + payload.cbHash = cb; + + hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE, CacheTestEventRoutine, CacheTestProgressRoutine, &context); + Assert::Equal(S_OK, hr); + } + finally + { + ReleaseMem(pb); + ReleaseStr(sczPayloadPath); + + String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File"); + if (File::Exists(filePath)) + { + File::SetAttributes(filePath, FileAttributes::Normal); + File::Delete(filePath); + } + } + } + }; +} +} +} +} +} + +static HRESULT CALLBACK CacheTestEventRoutine( + __in BURN_CACHE_MESSAGE* /*pMessage*/, + __in LPVOID /*pvContext*/ + ) +{ + return S_OK; +} + +static DWORD CALLBACK CacheTestProgressRoutine( + __in LARGE_INTEGER /*TotalFileSize*/, + __in LARGE_INTEGER /*TotalBytesTransferred*/, + __in LARGE_INTEGER /*StreamSize*/, + __in LARGE_INTEGER /*StreamBytesTransferred*/, + __in DWORD /*dwStreamNumber*/, + __in DWORD /*dwCallbackReason*/, + __in HANDLE /*hSourceFile*/, + __in HANDLE /*hDestinationFile*/, + __in_opt LPVOID /*lpData*/ + ) +{ + return PROGRESS_QUIET; +} diff --git a/src/burn/test/BurnUnitTest/ElevationTest.cpp b/src/burn/test/BurnUnitTest/ElevationTest.cpp new file mode 100644 index 00000000..3d144128 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ElevationTest.cpp @@ -0,0 +1,221 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE; +const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF; +const HRESULT S_TEST_SUCCEEDED = 0x3133; +const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}"; + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ); +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ); +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace System::IO; + using namespace System::Threading; + using namespace Xunit; + + public ref class ElevationTest : BurnUnitTest + { + public: + ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void ElevateTest() + { + HRESULT hr = S_OK; + BURN_PIPE_CONNECTION connection = { }; + HANDLE hEvent = NULL; + DWORD dwResult = S_OK; + try + { + ShelFunctionOverride(ElevateTest_ShellExecuteExW); + + PipeConnectionInitialize(&connection); + + // + // per-user side setup + // + hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret); + TestThrowOnFailure(hr, L"Failed to create connection name and secret."); + + hr = PipeCreatePipes(&connection, TRUE, &hEvent); + TestThrowOnFailure(hr, L"Failed to create pipes."); + + hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL); + TestThrowOnFailure(hr, L"Failed to create elevated process."); + + hr = PipeWaitForChildConnect(&connection); + TestThrowOnFailure(hr, L"Failed to wait for child process to connect."); + + // post execute message + hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult); + TestThrowOnFailure(hr, "Failed to post execute message to per-machine process."); + + // + // initiate termination + // + hr = PipeTerminateChildProcess(&connection, 666, FALSE); + TestThrowOnFailure(hr, L"Failed to terminate elevated process."); + + // check flags + Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult); + } + finally + { + PipeConnectionUninitialize(&connection); + ReleaseHandle(hEvent); + } + } + }; +} +} +} +} +} + + +static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW( + __inout LPSHELLEXECUTEINFOW lpExecInfo + ) +{ + HRESULT hr = S_OK; + LPWSTR scz = NULL; + + hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0); + ExitOnFailure(hr, "Failed to copy arguments."); + + // Pretend this thread is the elevated process. + lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL); + ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread."); + scz = NULL; + +LExit: + ReleaseStr(scz); + + return SUCCEEDED(hr); +} + +static DWORD CALLBACK ElevateTest_ThreadProc( + __in LPVOID lpThreadParameter + ) +{ + HRESULT hr = S_OK; + LPWSTR sczArguments = (LPWSTR)lpThreadParameter; + BURN_PIPE_CONNECTION connection = { }; + BURN_PIPE_RESULT result = { }; + + PipeConnectionInitialize(&connection); + + StrAlloc(&connection.sczName, MAX_PATH); + StrAlloc(&connection.sczSecret, MAX_PATH); + + // parse command line arguments + if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId)) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Failed to parse argument string."); + } + + // set up connection with per-user process + hr = PipeChildConnect(&connection, TRUE); + ExitOnFailure(hr, "Failed to connect to per-user process."); + + // pump messages + hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast(connection.hPipe), &result); + ExitOnFailure(hr, "Failed while pumping messages in child 'process'."); + +LExit: + PipeConnectionUninitialize(&connection); + ReleaseStr(sczArguments); + + return FAILED(hr) ? (DWORD)hr : result.dwResult; +} + +static HRESULT ProcessParentMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID /*pvContext*/, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HRESULT hrResult = E_INVALIDDATA; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_CHILD_SENT_MESSAGE_ID: + if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA))) + { + hrResult = S_TEST_SUCCEEDED; + } + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = static_cast(hrResult); + +LExit: + return hr; +} + +static HRESULT ProcessChildMessages( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + HANDLE hPipe = static_cast(pvContext); + DWORD dwResult = 0; + + // Process the message. + switch (pMsg->dwMessage) + { + case TEST_PARENT_SENT_MESSAGE_ID: + // send test message + hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult); + ExitOnFailure(hr, "Failed to send message to per-machine process."); + break; + + default: + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); + } + + *pdwResult = dwResult; + +LExit: + return hr; +} diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.cpp b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp new file mode 100644 index 00000000..96d5fab4 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestHelpers.cpp @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle) + { + HRESULT hr = S_OK; + IXMLDOMDocument* pixdDocument = NULL; + try + { + hr = XmlLoadDocument(wzDocument, &pixdDocument); + TestThrowOnFailure(hr, L"Failed to load XML document."); + + hr = pixdDocument->get_documentElement(ppixeBundle); + TestThrowOnFailure(hr, L"Failed to get bundle element."); + } + finally + { + ReleaseObject(pixdDocument); + } + } +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/ManifestHelpers.h b/src/burn/test/BurnUnitTest/ManifestHelpers.h new file mode 100644 index 00000000..e3e57555 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestHelpers.h @@ -0,0 +1,24 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle); + + +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/ManifestTest.cpp b/src/burn/test/BurnUnitTest/ManifestTest.cpp new file mode 100644 index 00000000..963be156 --- /dev/null +++ b/src/burn/test/BurnUnitTest/ManifestTest.cpp @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class ManifestTest : BurnUnitTest + { + public: + ManifestTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void ManifestLoadXmlTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + try + { + LPCSTR szDocument = + "" + " " + " " + " " + " " + " "; + + hr = VariableInitialize(&engineState.variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load manifest from XML + hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // check variable values + Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1")); + } + finally + { + //CoreUninitialize(&engineState); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp new file mode 100644 index 00000000..a7c1d83c --- /dev/null +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -0,0 +1,1473 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE message, + __in const LPVOID pvArgs, + __inout LPVOID pvResults, + __in_opt LPVOID pvContext + ); + +static LPCWSTR wzMsiTransactionManifestFileName = L"MsiTransaction_BundleAv1_manifest.xml"; +static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manifest.xml"; +static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class PlanTest : BurnUnitTest + { + public: + PlanTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void MsiTransactionInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"1.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 9); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 14); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(107082ull, pPlan->qwEstimatedSize); + Assert::Equal(506145ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[8].syncpoint.hEvent); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(4ul, pPlan->cExecutePackagesTotal); + Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void MsiTransactionUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzMsiTransactionManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteBeginMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCommitMsiTransaction(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"rbaOCA08D8ky7uBOK71_6FWz1K3TuQ", TRUE, TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(3ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{A497C5E5-C78B-4F0B-BF72-B33E1DB1C4B8}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{D1D01094-23CE-4AF0-84B6-4A1A133F21D3}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{01E6B748-7B95-4BA9-976D-B6F35076CEF4}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void RelatedBundleMissingFromCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + BURN_RELATED_BUNDLE* pRelatedBundle = DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + pRelatedBundle->fPlannable = FALSE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SingleMsiCacheTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_CACHE); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_CACHE, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(33743ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectUpgradeBundle(pEngineState, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", L"0.9.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(35694ull, pPlan->qwEstimatedSize); + Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteExePackage(pPlan, fRollback, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SingleMsiInstalledWithNoInstalledPackagesModifyTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectPackagesAsAbsent(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_MODIFY); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_MODIFY, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(1ul, pPlan->cExecutePackagesTotal); + Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + [Fact] + void SingleMsiUninstallTestFromUpgradeBundleWithSameExactPackage() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAsRelatedUpgradeBundle(&engineState, L"{02940F3E-C83E-452D-BFCF-C943777ACEAE}", L"2.0.0.0"); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_IGNORED, BURN_PACKAGE_REGISTRATION_STATE_IGNORED); + } + + [Fact] + void SlipstreamInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPermanentPackagesAsPresentAndCached(pEngineState); + PlanTestDetectPatchInitialize(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 2); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(3055111ull, pPlan->qwEstimatedSize); + Assert::Equal(212992ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 3; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[5].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitSyncpoint(pPlan, fRollback, dwIndex++, pPlan->rgCacheActions[2].syncpoint.hEvent); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 3; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + + [Fact] + void SlipstreamUninstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSlipstreamManifestFileName, pEngineState); + DetectPackagesAsPresentAndCached(pEngineState); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fDisableRollback); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 1; + BURN_EXECUTE_ACTION* pExecuteAction = NULL; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_UNREGISTER); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 1; + ValidateExecuteRollbackBoundary(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PatchA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PatchA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + pExecuteAction = ValidateDeletedExecuteMspTarget(pPlan, fRollback, dwIndex++, L"PatchA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", TRUE, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, TRUE); + ValidateExecuteMspTargetPatch(pExecuteAction, 0, L"PatchA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_REGISTER); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); + ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{22D1DDBA-284D-40A7-BD14-95EA07906F21}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{0A5113E3-06A5-4CE0-8E83-9EB42F6764A6}", NULL); + ValidatePlannedProvider(pPlan, uIndex++, L"{5FF7F534-3FFC-41E0-80CD-E6361E5E7B7B}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidatePermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"NetFx48Web"); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PatchA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); + } + + private: + // This doesn't initialize everything, just enough for CorePlan to work. + void InitializeEngineStateForCorePlan(LPCWSTR wzManifestFileName, BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + LPWSTR sczFilePath = NULL; + + ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); + + hr = VariableInitialize(&pEngineState->variables); + NativeAssert::Succeeded(hr, "Failed to initialize variables."); + + try + { + pin_ptr dataDirectory = PtrToStringChars(this->TestContext->TestDirectory); + hr = PathConcat(dataDirectory, L"TestData\\PlanTest", &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file directory."); + hr = PathConcat(sczFilePath, wzManifestFileName, &sczFilePath); + NativeAssert::Succeeded(hr, "Failed to get path to test file."); + Assert::True(FileExistsEx(sczFilePath, NULL), "Test file does not exist."); + + hr = ManifestLoadXmlFromFile(sczFilePath, pEngineState); + NativeAssert::Succeeded(hr, "Failed to load manifest."); + } + finally + { + ReleaseStr(sczFilePath); + } + + hr = CoreInitializeConstants(pEngineState); + NativeAssert::Succeeded(hr, "Failed to initialize core constants"); + + pEngineState->userExperience.pfnBAProc = PlanTestBAProc; + } + + void PlanTestDetect(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = S_OK; + BURN_REGISTRATION* pRegistration = &pEngineState->registration; + + DetectReset(pRegistration, &pEngineState->packages); + PlanReset(&pEngineState->plan, &pEngineState->containers, &pEngineState->packages, &pEngineState->layoutPayloads); + + hr = DepDependencyArrayAlloc(&pRegistration->rgIgnoredDependencies, &pRegistration->cIgnoredDependencies, pRegistration->sczProviderKey, NULL); + NativeAssert::Succeeded(hr, "Failed to add the bundle provider key to the list of dependencies to ignore."); + + pEngineState->userExperience.fEngineActive = TRUE; + pEngineState->fDetected = TRUE; + } + + void PlanTestDetectPatchInitialize(BURN_ENGINE_STATE* pEngineState) + { + HRESULT hr = MsiEngineDetectInitialize(&pEngineState->packages); + NativeAssert::Succeeded(hr, "MsiEngineDetectInitialize failed"); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + + if (BURN_PACKAGE_TYPE_MSP == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msp.cTargetProductCodes; ++j) + { + BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + j; + + if (BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN == pTargetProduct->patchPackageState) + { + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + } + } + } + } + } + + void DetectAttachedContainerAsAttached(BURN_ENGINE_STATE* pEngineState) + { + for (DWORD i = 0; i < pEngineState->containers.cContainers; ++i) + { + BURN_CONTAINER* pContainer = pEngineState->containers.rgContainers + i; + if (pContainer->fAttached) + { + pContainer->fActuallyAttached = TRUE; + } + } + } + + void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; + } + } + + void DetectPackageAsPresentAndCached(BURN_PACKAGE* pPackage) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pPackage->fCached = TRUE; + if (pPackage->fCanAffectRegistration) + { + pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + + void DetectPackageDependent(BURN_PACKAGE* pPackage, LPCWSTR wzId) + { + HRESULT hr = S_OK; + + for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) + { + BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + i; + + hr = DepDependencyArrayAlloc(&pProvider->rgDependents, &pProvider->cDependents, wzId, NULL); + NativeAssert::Succeeded(hr, "Failed to add package dependent"); + } + } + + void DetectPackagesAsAbsent(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageAsAbsent(pPackage); + } + } + + void DetectPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); + + if (BURN_PACKAGE_TYPE_MSI == pPackage->type) + { + for (DWORD j = 0; j < pPackage->Msi.cSlipstreamMspPackages; ++j) + { + pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; + + BURN_PACKAGE* pMspPackage = pPackage->Msi.rgSlipstreamMsps[j].pMspPackage; + MspEngineAddDetectedTargetProduct(&pEngineState->packages, pMspPackage, j, pPackage->Msi.sczProductCode, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED); + + BURN_MSPTARGETPRODUCT* pTargetProduct = pMspPackage->Msp.rgTargetProducts + (pMspPackage->Msp.cTargetProductCodes - 1); + pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; + pTargetProduct->registrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; + } + } + } + } + + void DetectPermanentPackagesAsPresentAndCached(BURN_ENGINE_STATE* pEngineState) + { + PlanTestDetect(pEngineState); + + pEngineState->registration.fInstalled = TRUE; + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + if (pPackage->fUninstallable) + { + DetectPackageAsAbsent(pPackage); + } + else + { + DetectPackageAsPresentAndCached(pPackage); + DetectPackageDependent(pPackage, pEngineState->registration.sczId); + } + } + } + + BURN_RELATED_BUNDLE* DetectUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = S_OK; + BURN_RELATED_BUNDLES* pRelatedBundles = &pEngineState->registration.relatedBundles; + BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; + + hr = StrAllocString(&dependencyProvider.sczKey, wzId, 0); + NativeAssert::Succeeded(hr, "Failed to copy provider key"); + + dependencyProvider.fImported = TRUE; + + hr = StrAllocString(&dependencyProvider.sczVersion, wzVersion, 0); + NativeAssert::Succeeded(hr, "Failed to copy version"); + + hr = MemEnsureArraySize(reinterpret_cast(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + NativeAssert::Succeeded(hr, "Failed to ensure there is space for related bundles."); + + BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; + + hr = VerParseVersion(wzVersion, 0, FALSE, &pRelatedBundle->pVersion); + NativeAssert::Succeeded(hr, "Failed to parse pseudo bundle version: %ls", wzVersion); + + pRelatedBundle->fPlannable = TRUE; + pRelatedBundle->relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + hr = PseudoBundleInitialize(0, &pRelatedBundle->package, TRUE, wzId, pRelatedBundle->relationType, BOOTSTRAPPER_PACKAGE_STATE_PRESENT, TRUE, NULL, NULL, NULL, 0, FALSE, L"-quiet", L"-repair -quiet", L"-uninstall -quiet", &dependencyProvider, NULL, 0); + NativeAssert::Succeeded(hr, "Failed to initialize related bundle to represent bundle: %ls", wzId); + + ++pRelatedBundles->cRelatedBundles; + + return pRelatedBundle; + } + + void DetectAsRelatedUpgradeBundle( + __in BURN_ENGINE_STATE* pEngineState, + __in LPCWSTR wzId, + __in LPCWSTR wzVersion + ) + { + HRESULT hr = StrAllocString(&pEngineState->registration.sczAncestors, wzId, 0); + NativeAssert::Succeeded(hr, "Failed to set registration's ancestors"); + + pEngineState->command.relationType = BOOTSTRAPPER_RELATION_UPGRADE; + + DetectPackagesAsPresentAndCached(pEngineState); + DetectUpgradeBundle(pEngineState, wzId, wzVersion); + + for (DWORD i = 0; i < pEngineState->packages.cPackages; ++i) + { + BURN_PACKAGE* pPackage = pEngineState->packages.rgPackages + i; + DetectPackageDependent(pPackage, wzId); + } + } + + void ValidateCacheContainer( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzContainerId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CONTAINER, pAction->type); + NativeAssert::StringEqual(wzContainerId, pAction->container.pContainer->sczId); + } + + BURN_CACHE_ACTION* ValidateCacheActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackCacheActions : pPlan->cCacheActions)); + return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; + } + + void ValidateCacheCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + } + + DWORD ValidateCachePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->package.pPackage->sczId); + return dwIndex + 1; + } + + void ValidateCacheRollbackPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->rollbackPackage.pPackage->sczId); + } + + void ValidateCacheSignalSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, pAction->type); + Assert::NotEqual((DWORD_PTR)NULL, (DWORD_PTR)pAction->syncpoint.hEvent); + } + + void ValidateCleanAction( + __in BURN_PLAN* pPlan, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); + + BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; + Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); + NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); + } + + BURN_EXECUTE_ACTION* ValidateExecuteActionExists(BURN_PLAN* pPlan, BOOL fRollback, DWORD dwIndex) + { + Assert::InRange(dwIndex + 1ul, 1ul, (fRollback ? pPlan->cRollbackActions : pPlan->cExecuteActions)); + return (fRollback ? pPlan->rgRollbackActions : pPlan->rgExecuteActions) + dwIndex; + } + + void ValidateExecuteBeginMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteCheckpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in DWORD dwId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_CHECKPOINT, pAction->type); + Assert::Equal(dwId, pAction->checkpoint.dwId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteCommitMsiTransaction( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzRollbackBoundaryId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, pAction->type); + NativeAssert::StringEqual(wzRollbackBoundaryId, pAction->msiTransaction.pRollbackBoundary->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteExePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in LPCWSTR wzIgnoreDependencies + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->exePackage.pPackage->sczId); + Assert::Equal(action, pAction->exePackage.action); + NativeAssert::StringEqual(wzIgnoreDependencies, pAction->exePackage.sczIgnoreDependencies); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteMsiPackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in DWORD dwLoggingAttributes + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->msiPackage.pPackage->sczId); + Assert::Equal(action, pAction->msiPackage.action); + Assert::Equal(actionMsiProperty, pAction->msiPackage.actionMsiProperty); + Assert::Equal(uiLevel, pAction->msiPackage.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->msiPackage.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->msiPackage.sczLogPath); + Assert::Equal(dwLoggingAttributes, pAction->msiPackage.dwLoggingAttributes); + Assert::Equal(FALSE, pAction->fDeleted); + } + + BURN_EXECUTE_ACTION* ValidateDeletedExecuteMspTarget( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId, + __in BOOTSTRAPPER_ACTION_STATE action, + __in_z LPCWSTR wzTargetProductCode, + __in BOOL fPerMachineTarget, + __in BURN_MSI_PROPERTY actionMsiProperty, + __in DWORD uiLevel, + __in BOOL fDisableExternalUiHandler, + __in BOOL fDeleted + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_MSP_TARGET, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->mspTarget.pPackage->sczId); + Assert::Equal(action, pAction->mspTarget.action); + NativeAssert::StringEqual(wzTargetProductCode, pAction->mspTarget.sczTargetProductCode); + Assert::Equal(fPerMachineTarget, pAction->mspTarget.fPerMachineTarget); + Assert::Equal(actionMsiProperty, pAction->mspTarget.actionMsiProperty); + Assert::Equal(uiLevel, pAction->mspTarget.uiLevel); + Assert::Equal(fDisableExternalUiHandler, pAction->mspTarget.fDisableExternalUiHandler); + NativeAssert::NotNull(pAction->mspTarget.sczLogPath); + Assert::Equal(fDeleted, pAction->fDeleted); + return pAction; + } + + void ValidateExecuteMspTargetPatch( + __in BURN_EXECUTE_ACTION* pAction, + __in DWORD dwIndex, + __in_z LPCWSTR wzPackageId + ) + { + Assert::InRange(dwIndex + 1ul, 1ul, pAction->mspTarget.cOrderedPatches); + BURN_ORDERED_PATCHES* pOrderedPatch = pAction->mspTarget.rgOrderedPatches + dwIndex; + NativeAssert::StringEqual(wzPackageId, pOrderedPatch->pPackage->sczId); + } + + void ValidateExecutePackageDependency( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in LPCWSTR wzBundleProviderKey, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageDependency.pPackage->sczId); + NativeAssert::StringEqual(wzBundleProviderKey, pAction->packageDependency.sczBundleProviderKey); + Assert::Equal(action, pAction->packageDependency.action); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecutePackageProvider( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId, + __in BURN_DEPENDENCY_ACTION action + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_PACKAGE_PROVIDER, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->packageProvider.pPackage->sczId); + Assert::Equal(action, pAction->packageProvider.action); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteRollbackBoundary( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzId, + __in BOOL fVital, + __in BOOL fTransaction + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY, pAction->type); + NativeAssert::StringEqual(wzId, pAction->rollbackBoundary.pRollbackBoundary->sczId); + Assert::Equal(fVital, pAction->rollbackBoundary.pRollbackBoundary->fVital); + Assert::Equal(fTransaction, pAction->rollbackBoundary.pRollbackBoundary->fTransaction); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteUncachePackage( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in LPCWSTR wzPackageId + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_UNCACHE_PACKAGE, pAction->type); + NativeAssert::StringEqual(wzPackageId, pAction->uncachePackage.pPackage->sczId); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateExecuteWaitSyncpoint( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex, + __in HANDLE hEvent + ) + { + BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_EXECUTE_ACTION_TYPE_WAIT_SYNCPOINT, pAction->type); + Assert::Equal((DWORD_PTR)hEvent, (DWORD_PTR)pAction->syncpoint.hEvent); + Assert::Equal(FALSE, pAction->fDeleted); + } + + void ValidateNonPermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId, + __in BURN_PACKAGE_REGISTRATION_STATE expectedCacheState, + __in BURN_PACKAGE_REGISTRATION_STATE expectedInstallState + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(TRUE, pPackage->fCanAffectRegistration); + Assert::Equal(expectedCacheState, pPackage->expectedCacheRegistrationState); + Assert::Equal(expectedInstallState, pPackage->expectedInstallRegistrationState); + } + + void ValidatePermanentPackageExpectedStates( + __in BURN_PACKAGE* pPackage, + __in_z LPCWSTR wzPackageId + ) + { + NativeAssert::StringEqual(wzPackageId, pPackage->sczId); + Assert::Equal(FALSE, pPackage->fCanAffectRegistration); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedCacheRegistrationState); + Assert::Equal(BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, pPackage->expectedInstallRegistrationState); + } + + void ValidatePlannedProvider( + __in BURN_PLAN* pPlan, + __in UINT uIndex, + __in LPCWSTR wzKey, + __in LPCWSTR wzName + ) + { + Assert::InRange(uIndex + 1u, 1u, pPlan->cPlannedProviders); + + DEPENDENCY* pProvider = pPlan->rgPlannedProviders + uIndex; + NativeAssert::StringEqual(wzKey, pProvider->sczKey); + NativeAssert::StringEqual(wzName, pProvider->sczName); + } + }; +} +} +} +} +} + +static HRESULT WINAPI PlanTestBAProc( + __in BOOTSTRAPPER_APPLICATION_MESSAGE /*message*/, + __in const LPVOID /*pvArgs*/, + __inout LPVOID /*pvResults*/, + __in_opt LPVOID /*pvContext*/ + ) +{ + return S_OK; +} diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp new file mode 100644 index 00000000..7b126f61 --- /dev/null +++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp @@ -0,0 +1,772 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest" +#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM" +#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU" +#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" +#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce" + +#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}" +#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ); +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ); +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ); + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace Microsoft::Win32; + using namespace System; + using namespace System::IO; + using namespace Xunit; + + public ref class RegistrationTest : BurnUnitTest + { + public: + RegistrationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void RegisterBasicTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::True(Directory::Exists(cacheDirectory)); + Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); + + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr))); + + // end session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::False(Directory::Exists(cacheDirectory)); + + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterArpMinimumTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterVariablesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // complete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration variables were updated + registration.fInstalled = TRUE; + + hr = RegistrationSetVariables(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to set registration variables."); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED)); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING)); + Assert::Equal(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG)); + Assert::Equal(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY)); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION)); + + // + // uninstall + // + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact] + void RegisterArpFullTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // + // install + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was created + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // finish registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr)); + Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr)); + Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr)); + Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr)); + Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr)); + Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr)); + Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr)); + Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr)); + + // + // uninstall + // + + // write registration + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + // verify that registration was updated + Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // delete registration + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // verify that registration was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + [Fact(Skip = "Currently fails")] + void ResumeTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + LPWSTR sczCurrentProcess = NULL; + BURN_VARIABLES variables = { }; + BURN_USER_EXPERIENCE userExperience = { }; + BOOTSTRAPPER_COMMAND command = { }; + BURN_REGISTRATION registration = { }; + BURN_LOGGING logging = { }; + BURN_PACKAGES packages = { }; + BYTE rgbData[256] = { }; + BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); + try + { + for (DWORD i = 0; i < 256; ++i) + { + rgbData[i] = (BYTE)i; + } + + // set mock API's + RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); + + Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); + + logging.sczPath = L"BurnUnitTest.txt"; + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = UserExperienceParseFromXml(&userExperience, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse UX from XML."); + + hr = RegistrationParseFromXml(®istration, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); + TestThrowOnFailure(hr, L"Failed to set registration resume command."); + + hr = PathForCurrentProcess(&sczCurrentProcess, NULL); + TestThrowOnFailure(hr, L"Failed to get current process path."); + + // read resume type before session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + + // begin session + hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); + TestThrowOnFailure(hr, L"Failed to register bundle."); + + hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData)); + TestThrowOnFailure(hr, L"Failed to save state."); + + // read interrupted resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read interrupted resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType); + + // suspend session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); + TestThrowOnFailure(hr, L"Failed to suspend session."); + + // verify that run key was removed + Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // read suspend resume type + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read suspend resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType); + + // read state back + hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to load state."); + + Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer); + Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData))); + + // write active resume mode + hr = RegistrationSessionResume(®istration, &variables); + TestThrowOnFailure(hr, L"Failed to write active resume mode."); + + // verify that run key was put back + Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); + + // end session + hr = RegistrationSessionEnd(®istration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); + TestThrowOnFailure(hr, L"Failed to unregister bundle."); + + // read resume type after session + hr = RegistrationDetectResumeType(®istration, &resumeType); + TestThrowOnFailure(hr, L"Failed to read resume type."); + + Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType); + } + finally + { + ReleaseStr(sczCurrentProcess); + ReleaseObject(pixeBundle); + UserExperienceUninitialize(&userExperience); + RegistrationUninitialize(®istration); + VariablesUninitialize(&variables); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); + if (Directory::Exists(cacheDirectory)) + { + Directory::Delete(cacheDirectory, true); + } + + RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + } + } + + //BOOTSTRAPPER_RESUME_TYPE_NONE, + //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid + //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption + //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet + //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot + //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend + //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP + }; +} +} +} +} +} + + +static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __reserved DWORD Reserved, + __in_opt LPWSTR lpClass, + __in DWORD dwOptions, + __in REGSAM samDesired, + __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, + __out PHKEY phkResult, + __out_opt LPDWORD lpdwDisposition + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW( + __in HKEY hKey, + __in_opt LPCWSTR lpSubKey, + __reserved DWORD ulOptions, + __in REGSAM samDesired, + __out PHKEY phkResult + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} + +static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW( + __in HKEY hKey, + __in LPCWSTR lpSubKey, + __in REGSAM samDesired, + __reserved DWORD Reserved + ) +{ + LSTATUS ls = ERROR_SUCCESS; + LPCWSTR wzRoot = NULL; + HKEY hkRoot = NULL; + + if (HKEY_LOCAL_MACHINE == hKey) + { + wzRoot = HKLM_PATH; + } + else if (HKEY_CURRENT_USER == hKey) + { + wzRoot = HKCU_PATH; + } + else + { + hkRoot = hKey; + } + + if (wzRoot) + { + ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot); + if (ERROR_SUCCESS != ls) + { + ExitFunction(); + } + } + + ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved); + +LExit: + ReleaseRegKey(hkRoot); + + return ls; +} diff --git a/src/burn/test/BurnUnitTest/SearchTest.cpp b/src/burn/test/BurnUnitTest/SearchTest.cpp new file mode 100644 index 00000000..eca01f5f --- /dev/null +++ b/src/burn/test/BurnUnitTest/SearchTest.cpp @@ -0,0 +1,815 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR szUserSid, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ); + +using namespace System; +using namespace Xunit; +using namespace Microsoft::Win32; + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + public ref class SearchTest : BurnUnitTest + { + public: + SearchTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void DirectorySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory); + pin_ptr wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none"))); + + VariableSetStringHelper(&variables, L"Directory1", wzDirectory1, FALSE); + VariableSetStringHelper(&variables, L"Directory2", wzDirectory2, FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact(Skip = "Currently fails")] + void FileSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + ULARGE_INTEGER uliVersion = { }; + VERUTIL_VERSION* pVersion = NULL; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + pin_ptr wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt"))); + pin_ptr wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location); + + hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart); + TestThrowOnFailure(hr, L"Failed to get DLL version."); + + hr = VerVersionFromQword(uliVersion.QuadPart, &pVersion); + NativeAssert::Succeeded(hr, "Failed to create version."); + + VariableSetStringHelper(&variables, L"File1", wzFile1, FALSE); + VariableSetStringHelper(&variables, L"File2", wzFile2, FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(gcnew String(pVersion->sczVersion), VariableGetVersionHelper(&variables, L"Variable3")); + } + finally + { + ReleaseVerutilVersion(pVersion); + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void RegistrySearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + HKEY hkey32 = NULL; + HKEY hkey64 = NULL; + BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)")); + + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String); + Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord); + + if (f64bitMachine) + { + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32); + Assert::True(SUCCEEDED(hr)); + + hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit"); + Assert::True(SUCCEEDED(hr)); + + hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit"); + Assert::True(SUCCEEDED(hr)); + } + + VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value", FALSE); + VariableSetStringHelper(&variables, L"MyValue", L"String", FALSE); + VariableSetStringHelper(&variables, L"Variable27", L"Default27", FALSE); + VariableSetStringHelper(&variables, L"Variable28", L"Default28", FALSE); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable13")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16")); + Assert::False(VariableExistsHelper(&variables, L"Variable17")); + Assert::False(VariableExistsHelper(&variables, L"Variable18")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19")); + Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20")); + if (f64bitMachine) + { + Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21")); + Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22")); + } + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23")); + Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25")); + Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable26")); + Assert::Equal(gcnew String(L"Default27"), VariableGetStringHelper(&variables, L"Variable27")); + Assert::Equal(gcnew String(L"Default28"), VariableGetStringHelper(&variables, L"Variable28")); + } + finally + { + ReleaseRegKey(hkey32); + ReleaseRegKey(hkey64); + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + + Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest")); + if (f64bitMachine) + { + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE); + RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE); + } + } + } + + [Fact] + void MsiComponentSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " // todo: value key path + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4")); + Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6")); + Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8")); + Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10")); + Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12")); + Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17")); + Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18")); + Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void MsiProductSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set mock API's + WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable2")); + Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3")); + Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5")); + Assert::Equal(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, L"Variable6")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void MsiFeatureSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void ConditionalSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::False(VariableExistsHelper(&variables, L"Variable1")); + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2")); + Assert::False(VariableExistsHelper(&variables, L"Variable3")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + [Fact] + void NoSearchesTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + + [Fact] + void SetVariableSearchTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BURN_SEARCHES searches = { }; + BURN_EXTENSIONS burnExtensions = { }; + try + { + LPCWSTR wzDocument = + L"" + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L" " + L""; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetNumericHelper(&variables, L"REMOVED_NUMBER", 22); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = SearchesParseFromXml(&searches, &burnExtensions, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse searches from XML."); + + // execute searches + hr = SearchesExecute(&searches, &variables); + TestThrowOnFailure(hr, L"Failed to execute searches."); + + // check variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"REMOVED_NUMBER")); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + SearchesUninitialize(&searches); + } + } + }; +} +} +} +} +} + + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW( + __in LPCWSTR szProduct, + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ product = gcnew String(szProduct); + + if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf); + } + + return is; +} + +static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW( + __in LPCWSTR szComponent, + __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf, + __inout_opt LPDWORD pcchBuf + ) +{ + HRESULT hr = S_OK; + INSTALLSTATE is = INSTALLSTATE_INVALIDARG; + String^ component = gcnew String(szComponent); + LPCWSTR wzValue = NULL; + + if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}"))) + { + is = INSTALLSTATE_UNKNOWN; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file1.txt"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file2.txt"; + is = INSTALLSTATE_SOURCE; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file3.txt"; + is = INSTALLSTATE_SOURCEABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\file4.txt"; + is = INSTALLSTATE_ABSENT; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}"))) + { + wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"; + is = INSTALLSTATE_LOCAL; + } + else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}"))) + { + wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"; + is = INSTALLSTATE_ABSENT; + } + + if (wzValue && lpPathBuf) + { + hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchBuf = lstrlenW(wzValue); + is = INSTALLSTATE_MOREDATA; + } + else if (FAILED(hr)) + { + is = INSTALLSTATE_INVALIDARG; + } + } + + return is; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW( + __in LPCWSTR szProductCode, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) && + String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // force call to WiuGetProductInfoEx + return ERROR_UNKNOWN_PROPERTY; + } + + UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue); + return er; +} + +static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW( + __in LPCWSTR szProductCode, + __in_opt LPCWSTR /*szUserSid*/, + __in MSIINSTALLCONTEXT dwContext, + __in LPCWSTR szProperty, + __out_ecount_opt(*pcchValue) LPWSTR szValue, + __inout_opt LPDWORD pcchValue + ) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_FUNCTION_FAILED; + LPCWSTR wzValue = NULL; + + String^ productCode = gcnew String(szProductCode); + String^ _property = gcnew String(szProperty); + switch (dwContext) + { + case MSIINSTALLCONTEXT_USERMANAGED: + er = ERROR_UNKNOWN_PRODUCT; + break; + case MSIINSTALLCONTEXT_USERUNMANAGED: + if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + wzValue = L"5"; + } + } + break; + case MSIINSTALLCONTEXT_MACHINE: + if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}"))) + { + er = ERROR_UNKNOWN_PRODUCT; + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}"))) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE))) + { + wzValue = L"1033"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE))) + { + wzValue = L"1"; + } + else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE))) + { + // try again in per-user context + er = ERROR_UNKNOWN_PRODUCT; + } + } + else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}"))) + { + static BOOL fFlipp = FALSE; + if (fFlipp) + { + if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING))) + { + wzValue = L"1.0.0.0"; + } + } + else + { + *pcchValue = MAX_PATH * 2; + er = ERROR_MORE_DATA; + } + fFlipp = !fFlipp; + } + break; + } + + if (wzValue) + { + hr = ::StringCchCopyW(szValue, *pcchValue, wzValue); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + *pcchValue = lstrlenW(wzValue); + er = ERROR_MORE_DATA; + } + else if (SUCCEEDED(hr)) + { + er = ERROR_SUCCESS; + } + } + + return er; +} diff --git a/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File new file mode 100644 index 00000000..896ac017 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/CacheTest/CacheSignatureTest.File @@ -0,0 +1 @@ +This file has a known hash. \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml new file mode 100644 index 00000000..65e3c63d --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BasicFunctionality_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml new file mode 100644 index 00000000..cca9a982 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/MsiTransaction_BundleAv1_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml new file mode 100644 index 00000000..996976b2 --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/Slipstream_BundleA_manifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.cpp b/src/burn/test/BurnUnitTest/VariableHelpers.cpp new file mode 100644 index 00000000..40f958f8 --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableHelpers.cpp @@ -0,0 +1,217 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +using namespace System; +using namespace Xunit; + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted) + { + HRESULT hr = S_OK; + + hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE, fFormatted); + TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue); + } + + void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue) + { + HRESULT hr = S_OK; + + hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue); + } + + void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + try + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + TestThrowOnFailure1(hr, L"Failed to parse version '%ls'", wzValue); + + hr = VariableSetVersion(pVariables, wzVariable, pVersion, FALSE); + TestThrowOnFailure2(hr, L"Failed to set %s to: '%ls'", wzVariable, wzValue); + } + finally + { + ReleaseVerutilVersion(pVersion); + } + } + + String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetString(pVariables, wzVariable, &scz); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + + hr = VariableGetNumeric(pVariables, wzVariable, &llValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return llValue; + } + + String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pValue = NULL; + + try + { + hr = VariableGetVersion(pVariables, wzVariable, &pValue); + TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable); + + return gcnew String(pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } + } + + String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableGetFormatted(pVariables, wzVariable, &scz, pfContainsHiddenVariable); + TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableFormatString(pVariables, wzIn, &scz, NULL); + TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + String^ VariableEscapeStringHelper(LPCWSTR wzIn) + { + HRESULT hr = S_OK; + LPWSTR scz = NULL; + try + { + hr = VariableEscapeString(wzIn, &scz); + TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn); + + return gcnew String(scz); + } + finally + { + ReleaseStr(scz); + } + } + + bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition); + + return f ? true : false; + } + + bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition) + { + HRESULT hr = S_OK; + BOOL f = FALSE; + + hr = ConditionEvaluate(pVariables, wzCondition, &f); + return E_INVALIDDATA == hr ? true : false; + } + + bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE) + { + return false; + } + else + { + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + return true; + } + } + finally + { + BVariantUninitialize(&value); + } + } + + int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable) + { + HRESULT hr = S_OK; + BURN_VARIANT value = { }; + + try + { + hr = VariableGetVariant(pVariables, wzVariable, &value); + TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable); + + return (int)value.Type; + } + finally + { + BVariantUninitialize(&value); + } + } +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariableHelpers.h b/src/burn/test/BurnUnitTest/VariableHelpers.h new file mode 100644 index 00000000..d460c60f --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableHelpers.h @@ -0,0 +1,36 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + + +void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue, BOOL fFormatted); +void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue); +void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue); +System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, BOOL* pfContainsHiddenVariable); +System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn); +System::String^ VariableEscapeStringHelper(LPCWSTR wzIn); +bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition); +bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); +int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable); + + +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariableTest.cpp b/src/burn/test/BurnUnitTest/VariableTest.cpp new file mode 100644 index 00000000..5c9dce03 --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariableTest.cpp @@ -0,0 +1,532 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" +#undef GetTempPath +#undef GetEnvironmentVariable + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariableTest : BurnUnitTest + { + public: + VariableTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void VariablesBasicTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetNumericHelper(&variables, L"PROP2", 2); + VariableSetStringHelper(&variables, L"PROP5", L"VAL5", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"VAL6", FALSE); + VariableSetStringHelper(&variables, L"PROP7", L"7", FALSE); + VariableSetVersionHelper(&variables, L"PROP8", L"1.1.0.0"); + VariableSetStringHelper(&variables, L"PROP9", L"[VAL9]", TRUE); + + // set overwritten variables + VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL", FALSE); + VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42); + + VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5); + VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW", FALSE); + + // get and verify variable values + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2")); + Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4")); + Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5")); + Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6")); + Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetVersionHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8")); + Assert::Equal(gcnew String(L"[VAL9]"), VariableGetStringHelper(&variables, L"PROP9")); + + Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING")); + Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesParseXmlTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_VARIABLES variables = { }; + BOOL fContainsHiddenData = FALSE; + try + { + LPCWSTR wzDocument = + L"" + L" "; + + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = VariablesParseFromXml(&variables, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse variables from XML."); + + // get and verify variable values + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3")); + Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables, L"Var6")); + + Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1")); + Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2")); + Assert::Equal(gcnew String(L"1.2.3.4"), VariableGetVersionHelper(&variables, L"Var3")); + Assert::Equal(gcnew String(L"[Formatted]"), VariableGetStringHelper(&variables, L"Var6")); + Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Formatted", &fContainsHiddenData)); + Assert::Equal(TRUE, fContainsHiddenData); + Assert::Equal(gcnew String(L"supersecret"), VariableGetFormattedHelper(&variables, L"Var6", &fContainsHiddenData)); + Assert::Equal(TRUE, fContainsHiddenData); + Assert::Equal(gcnew String(L"String value."), VariableGetFormattedHelper(&variables, L"Var2", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + } + finally + { + ReleaseObject(pixeBundle); + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesFormatTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + LPWSTR scz = NULL; + SIZE_T cch = 0; + BOOL fContainsHiddenData = FALSE; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); + VariableSetNumericHelper(&variables, L"PROP3", 3); + VariableSetStringHelper(&variables, L"PROP4", L"[PROP1]", FALSE); + VariableSetStringHelper(&variables, L"PROP5", L"[PROP2]", FALSE); + VariableSetStringHelper(&variables, L"PROP6", L"[PROP4]", TRUE); + VariableSetStringHelper(&variables, L"PROP7", L"[PROP5]", TRUE); + + // test string formatting + Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP")); + Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]")); + Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] ")); + Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]")); + Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST")); + Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST")); + Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]")); + Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]")); + Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]")); + Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]")); + Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]")); + Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE")); + Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP4", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP5", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetFormattedHelper(&variables, L"PROP6", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + Assert::Equal(gcnew String(L"[PROP2]"), VariableGetFormattedHelper(&variables, L"PROP7", &fContainsHiddenData)); + Assert::Equal(FALSE, fContainsHiddenData); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((SIZE_T)lstrlenW(scz), cch); + + hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch); + TestThrowOnFailure(hr, L"Failed to format string"); + + Assert::Equal((SIZE_T)lstrlenW(scz), cch); + } + finally + { + VariablesUninitialize(&variables); + ReleaseStr(scz); + } + } + + [Fact] + void VariablesEscapeTest() + { + // test string escaping + Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"[")); + Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]")); + Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] ")); + } + + [Fact] + void VariablesConditionTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // set variables + VariableSetStringHelper(&variables, L"PROP1", L"VAL1", FALSE); + VariableSetStringHelper(&variables, L"PROP2", L"VAL2", FALSE); + VariableSetStringHelper(&variables, L"PROP3", L"VAL3", FALSE); + VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END", FALSE); + VariableSetNumericHelper(&variables, L"PROP5", 5); + VariableSetNumericHelper(&variables, L"PROP6", 6); + VariableSetStringHelper(&variables, L"PROP7", L"", FALSE); + VariableSetNumericHelper(&variables, L"PROP8", 0); + VariableSetStringHelper(&variables, L"_PROP9", L"VAL9", FALSE); + VariableSetNumericHelper(&variables, L"PROP10", -10); + VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll); + VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll); + VariableSetNumericHelper(&variables, L"PROP13", 0x00010000); + VariableSetNumericHelper(&variables, L"PROP14", 0x00000001); + VariableSetNumericHelper(&variables, L"PROP15", 0x00010001); + VariableSetVersionHelper(&variables, L"PROP16", L"0.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP17", L"1.0.0.0"); + VariableSetVersionHelper(&variables, L"PROP18", L"1.1.0.0"); + VariableSetVersionHelper(&variables, L"PROP19", L"1.1.1.0"); + VariableSetVersionHelper(&variables, L"PROP20", L"1.1.1.1"); + VariableSetNumericHelper(&variables, L"vPROP21", 1); + VariableSetVersionHelper(&variables, L"PROP22", L"65535.65535.65535.65535"); + VariableSetStringHelper(&variables, L"PROP23", L"1.1.1", FALSE); + VariableSetStringHelper(&variables, L"PROP24", L"[PROP1]", TRUE); + VariableSetStringHelper(&variables, L"PROP25", L"[PROP7]", TRUE); + VariableSetStringHelper(&variables, L"PROP26", L"[PROP8]", TRUE); + VariableSetStringHelper(&variables, L"PROP27", L"[PROP16]", TRUE); + + // test conditions + Assert::True(EvaluateConditionHelper(&variables, L"PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP7")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP8")); + Assert::True(EvaluateConditionHelper(&variables, L"_PROP9")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP16")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP24=\"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP25")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP26")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP27")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"VAL1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"Val1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"NONE ~<> \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP20 > v1.1.1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23")); + Assert::False(EvaluateConditionHelper(&variables, L"v1.1.1<>PROP23")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1")); + Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000")); + + Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65536.65535.65535.65535")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP22 < v65535.655350000.65535.65535")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\"")); + Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\"")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0")); + + Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1")); + Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\"")); + + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\"")); + Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\"")); + Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"=")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \"")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"1A")); + Assert::True(EvaluateFailureConditionHelper(&variables, L"*")); + + Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1")); + } + finally + { + VariablesUninitialize(&variables); + } + } + + [Fact] + void VariablesSerializationTest() + { + HRESULT hr = S_OK; + BYTE* pbBuffer = NULL; + SIZE_T cbBuffer = 0; + SIZE_T iBuffer = 0; + BURN_VARIABLES variables1 = { }; + BURN_VARIABLES variables2 = { }; + try + { + // serialize + hr = VariableInitialize(&variables1); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + VariableSetStringHelper(&variables1, L"PROP1", L"VAL1", FALSE); + VariableSetNumericHelper(&variables1, L"PROP2", 2); + VariableSetVersionHelper(&variables1, L"PROP3", L"1.1.1.1"); + VariableSetStringHelper(&variables1, L"PROP4", L"VAL4", FALSE); + VariableSetStringHelper(&variables1, L"PROP5", L"[PROP1]", TRUE); + + hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer); + TestThrowOnFailure(hr, L"Failed to serialize variables."); + + // deserialize + hr = VariableInitialize(&variables2); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer); + TestThrowOnFailure(hr, L"Failed to deserialize variables."); + + Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1")); + Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2")); + Assert::Equal(gcnew String(L"1.1.1.1"), VariableGetVersionHelper(&variables2, L"PROP3")); + Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4")); + Assert::Equal(gcnew String(L"[PROP1]"), VariableGetStringHelper(&variables2, L"PROP5")); + + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP1")); + Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables2, L"PROP2")); + Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables2, L"PROP3")); + Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables2, L"PROP4")); + Assert::Equal((int)BURN_VARIANT_TYPE_FORMATTED, VariableGetTypeHelper(&variables2, L"PROP5")); + } + finally + { + ReleaseBuffer(pbBuffer); + VariablesUninitialize(&variables1); + VariablesUninitialize(&variables2); + } + } + + [Fact] + void VariablesBuiltInTest() + { + HRESULT hr = S_OK; + BURN_VARIABLES variables = { }; + try + { + hr = VariableInitialize(&variables); + TestThrowOnFailure(hr, L"Failed to initialize variables."); + + // VersionMsi + Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1")); + + // VersionNT + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0")); + + // VersionNT64 + if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)")) + { + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + else + { + Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64")); + } + + // attempt to set a built-in property + hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE, FALSE); + Assert::Equal(E_INVALIDARG, hr); + Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\"")); + + VariableGetNumericHelper(&variables, L"NTProductType"); + VariableGetNumericHelper(&variables, L"NTSuiteBackOffice"); + VariableGetNumericHelper(&variables, L"NTSuiteDataCenter"); + VariableGetNumericHelper(&variables, L"NTSuiteEnterprise"); + VariableGetNumericHelper(&variables, L"NTSuitePersonal"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness"); + VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted"); + VariableGetNumericHelper(&variables, L"NTSuiteWebServer"); + VariableGetNumericHelper(&variables, L"CompatibilityMode"); + VariableGetNumericHelper(&variables, L"Privileged"); + VariableGetNumericHelper(&variables, L"SystemLanguageID"); + VariableGetNumericHelper(&variables, L"TerminalServer"); + VariableGetNumericHelper(&variables, L"UserUILanguageID"); + VariableGetNumericHelper(&variables, L"UserLanguageID"); + + // known folders + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder")); + + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder")); + VariableGetStringHelper(&variables, L"FontsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder")); + VariableGetStringHelper(&variables, L"SystemFolder"); + VariableGetStringHelper(&variables, L"WindowsFolder"); + VariableGetStringHelper(&variables, L"WindowsVolume"); + + Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder"))); + + VariableGetStringHelper(&variables, L"AdminToolsFolder"); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder")); + Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder")); + + if (Environment::Is64BitOperatingSystem) + { + VariableGetStringHelper(&variables, L"ProgramFiles64Folder"); + VariableGetStringHelper(&variables, L"CommonFiles64Folder"); + VariableGetStringHelper(&variables, L"System64Folder"); + } + } + finally + { + VariablesUninitialize(&variables); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/VariantTest.cpp b/src/burn/test/BurnUnitTest/VariantTest.cpp new file mode 100644 index 00000000..43899a2b --- /dev/null +++ b/src/burn/test/BurnUnitTest/VariantTest.cpp @@ -0,0 +1,221 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +namespace Microsoft +{ +namespace Tools +{ +namespace WindowsInstallerXml +{ +namespace Test +{ +namespace Bootstrapper +{ + using namespace System; + using namespace Xunit; + + public ref class VariantTest : BurnUnitTest + { + public: + VariantTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture) + { + } + + [Fact] + void VariantBasicTest() + { + BURN_VARIANT expectedVariants[10]; + BURN_VARIANT actualVariants[10]; + for (DWORD i = 0; i < 10; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + + try + { + InitNumericValue(expectedVariants + 0, 2, FALSE, L"PROP1", actualVariants + 0); + InitStringValue(expectedVariants + 1, L"VAL2", FALSE, L"PROP2", actualVariants + 1); + InitVersionValue(expectedVariants + 2, L"1.1.0.0", FALSE, L"PROP3", actualVariants + 2); + InitNoneValue(expectedVariants + 3, FALSE, L"PROP4", actualVariants + 3); + InitNoneValue(expectedVariants + 4, TRUE, L"PROP5", actualVariants + 4); + InitVersionValue(expectedVariants + 5, L"1.1.1.0", TRUE, L"PROP6", actualVariants + 5); + InitStringValue(expectedVariants + 6, L"7", TRUE, L"PROP7", actualVariants + 6); + InitNumericValue(expectedVariants + 7, 11, TRUE, L"PROP8", actualVariants + 7); + InitFormattedValue(expectedVariants + 8, L"VAL9", FALSE, L"PROP9", actualVariants + 8); + InitFormattedValue(expectedVariants + 9, L"VAL10", TRUE, L"PROP10", actualVariants + 9); + + VerifyNumericValue(expectedVariants + 0, actualVariants + 0); + VerifyStringValue(expectedVariants + 1, actualVariants + 1); + VerifyVersionValue(expectedVariants + 2, actualVariants + 2); + VerifyNoneValue(expectedVariants + 3, actualVariants + 3); + VerifyNoneValue(expectedVariants + 4, actualVariants + 4); + VerifyVersionValue(expectedVariants + 5, actualVariants + 5); + VerifyStringValue(expectedVariants + 6, actualVariants + 6); + VerifyNumericValue(expectedVariants + 7, actualVariants + 7); + VerifyFormattedValue(expectedVariants + 8, actualVariants + 8); + VerifyFormattedValue(expectedVariants + 9, actualVariants + 9); + } + finally + { + for (DWORD i = 0; i < 10; i++) + { + BVariantUninitialize(expectedVariants + i); + BVariantUninitialize(actualVariants + i); + } + } + } + + private: + void InitFormattedValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_FORMATTED; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitNoneValue(BURN_VARIANT* pValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NONE; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitNumericValue(BURN_VARIANT* pValue, LONGLONG llValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_NUMERIC; + pValue->llValue = llValue; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitStringValue(BURN_VARIANT* pValue, LPWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + pValue->Type = BURN_VARIANT_TYPE_STRING; + + hr = StrAllocString(&pValue->sczValue, wzValue, 0); + NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue); + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + + void InitVersionValue(BURN_VARIANT* pValue, LPCWSTR wzValue, BOOL /*fHidden*/, LPCWSTR wz, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pVersion = NULL; + + try + { + hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); + NativeAssert::Succeeded(hr, "Failed to parse version {0}", wzValue); + + pValue->Type = BURN_VARIANT_TYPE_VERSION; + pValue->pValue = pVersion; + pVersion = NULL; + + hr = BVariantCopy(pValue, pActualValue); + NativeAssert::Succeeded(hr, "Failed to copy variant {0}", wz); + } + finally + { + ReleaseVerutilVersion(pVersion); + } + } + + void VerifyFormattedValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_FORMATTED, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get string value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + + void VerifyNumericValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LONGLONG llValue = 0; + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NUMERIC, pActualValue->Type); + + hr = BVariantGetNumeric(pActualValue, &llValue); + NativeAssert::Succeeded(hr, "Failed to get numeric value"); + + NativeAssert::Equal(pExpectedValue->llValue, llValue); + } + + void VerifyNoneValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_NONE, pActualValue->Type); + NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue); + } + + void VerifyStringValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + LPWSTR sczValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_STRING, pActualValue->Type); + + try + { + hr = BVariantGetString(pActualValue, &sczValue); + NativeAssert::Succeeded(hr, "Failed to get string value"); + + NativeAssert::StringEqual(pExpectedValue->sczValue, sczValue); + } + finally + { + ReleaseStr(sczValue); + } + } + + void VerifyVersionValue(BURN_VARIANT* pExpectedValue, BURN_VARIANT* pActualValue) + { + HRESULT hr = S_OK; + VERUTIL_VERSION* pValue = NULL; + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pExpectedValue->Type); + NativeAssert::Equal(BURN_VARIANT_TYPE_VERSION, pActualValue->Type); + + try + { + hr = BVariantGetVersion(pActualValue, &pValue); + NativeAssert::Succeeded(hr, "Failed to get version value"); + + NativeAssert::StringEqual(pExpectedValue->pValue->sczVersion, pActualValue->pValue->sczVersion); + } + finally + { + ReleaseVerutilVersion(pValue); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/packages.config b/src/burn/test/BurnUnitTest/packages.config new file mode 100644 index 00000000..1d36c387 --- /dev/null +++ b/src/burn/test/BurnUnitTest/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/burn/test/BurnUnitTest/precomp.cpp b/src/burn/test/BurnUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/burn/test/BurnUnitTest/precomp.cpp @@ -0,0 +1,3 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h new file mode 100644 index 00000000..d2b57d61 --- /dev/null +++ b/src/burn/test/BurnUnitTest/precomp.h @@ -0,0 +1,79 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wininet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BootstrapperEngine.h" +#include "BootstrapperApplication.h" +#include "BundleExtensionEngine.h" +#include "BundleExtension.h" + +#include "platform.h" +#include "variant.h" +#include "variable.h" +#include "condition.h" +#include "section.h" +#include "approvedexe.h" +#include "container.h" +#include "payload.h" +#include "cabextract.h" +#include "burnextension.h" +#include "search.h" +#include "userexperience.h" +#include "package.h" +#include "update.h" +#include "pseudobundle.h" +#include "registration.h" +#include "plan.h" +#include "pipe.h" +#include "logging.h" +#include "core.h" +#include "cache.h" +#include "apply.h" +#include "exeengine.h" +#include "msiengine.h" +#include "mspengine.h" +#include "msuengine.h" +#include "dependency.h" +#include "elevation.h" +#include "embedded.h" +#include "manifest.h" +#include "splashscreen.h" +#include "detect.h" + +#pragma managed +#include + +#include "BurnTestException.h" +#include "BurnTestFixture.h" +#include "BurnUnitTest.h" +#include "VariableHelpers.h" +#include "ManifestHelpers.h" -- cgit v1.2.3-55-g6feb