// 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 ROOT_PATH L"\\HKLM"
#define HKCU_PATH ROOT_PATH L"\\HKCU"
#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
#define TEST_BUNDLE_ID L"{D54F896D-1952-43E6-9C67-B5652240618C}"
#define TEST_BUNDLE_UPGRADE_CODE L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}"
#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\" TEST_BUNDLE_ID
#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY
#define TEST_VARIABLE_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\" TEST_BUNDLE_ID L"\\variables"
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 = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
DWORD dwRegistrationOptions = BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE;
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 = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
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, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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.clean.room /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr)));
// end session
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
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(TEST_BUNDLE_ID), 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 = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
DWORD dwRegistrationOptions = 0;
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 = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
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, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
TestThrowOnFailure(hr, L"Failed to register bundle.");
// verify that registration was created
Assert::Equal(gcnew String(L"Product1 Installation"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
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.clean.room /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
// complete registration
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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(TEST_BUNDLE_ID), nullptr));
//
// uninstall
//
// write registration
hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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.clean.room /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
// delete registration
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
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(TEST_BUNDLE_ID), 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 = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
DWORD dwRegistrationOptions = 0;
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 = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
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, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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.clean.room /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
// complete registration
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
TestThrowOnFailure(hr, L"Failed to unregister bundle.");
// verify that registration variables were updated
Assert::Equal(gcnew String(L"Product1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
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(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, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
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(TEST_BUNDLE_ID), 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 = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
DWORD dwRegistrationOptions = 0;
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 = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
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, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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.clean.room /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
// finish registration
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
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(TEST_BUNDLE_ID), 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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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.clean.room /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
// delete registration
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
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(TEST_BUNDLE_ID), 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 DUtilButilTest()
{
HRESULT hr = S_OK;
IXMLDOMElement* pixeBundle = NULL;
LPWSTR sczCurrentProcess = NULL;
LPWSTR sczValue = NULL;
LPWSTR sczRelatedBundleId = NULL;
DWORD dwRelatedBundleIndex = 0;
BURN_VARIABLES variables = { };
BURN_USER_EXPERIENCE userExperience = { };
BOOTSTRAPPER_COMMAND command = { };
BURN_REGISTRATION registration = { };
BURN_LOGGING logging = { };
BURN_PACKAGES packages = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
BYTE* pbBuffer = NULL;
SIZE_T cbBuffer = 0;
DWORD dwRegistrationOptions = 0;
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
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" "
L" "
L" "
L" "
L" "
L" "
L"";
// load XML document
LoadBundleXmlHelper(wzDocument, &pixeBundle);
hr = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
hr = VariableInitialize(&variables);
TestThrowOnFailure(hr, L"Failed to initialize variables.");
hr = VariablesParseFromXml(&variables, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
hr = RegistrationParseFromXml(®istration, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &logging);
TestThrowOnFailure(hr, L"Failed to set registration resume command.");
hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
TestThrowOnFailure(hr, L"Failed to get current process path.");
// begin session
hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
TestThrowOnFailure(hr, L"Failed to register bundle.");
VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
VariableSetStringHelper(&variables, L"MyBurnVariable2", L"bar", FALSE);
VariableSetVersionHelper(&variables, L"MyBurnVariable3", L"v1.0-beta");
hr = VariableSerialize(&variables, TRUE, &pbBuffer, &cbBuffer);
TestThrowOnFailure(hr, "Failed to serialize variables.");
if (!Directory::Exists(cacheDirectory))
{
Directory::CreateDirectory(cacheDirectory);
}
hr = RegistrationSaveState(®istration, pbBuffer, cbBuffer);
TestThrowOnFailure(hr, L"Failed to save state.");
ReleaseNullBuffer(pbBuffer);
cbBuffer = 0;
// Verify the variables exist
Assert::Equal(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
Assert::Equal(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
Assert::Equal(gcnew String(L"1.0-beta"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable3"), nullptr));
Assert::Empty((System::Collections::IEnumerable ^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"WixBundleForcedRestartPackage"), nullptr));
hr = StrAlloc(&sczRelatedBundleId, MAX_GUID_CHARS + 1);
NativeAssert::Succeeded(hr, "Failed to allocate buffer for related bundle id.");
// Verify we can find ourself via the UpgradeCode
hr = BundleEnumRelatedBundleFixed(TEST_BUNDLE_UPGRADE_CODE, BUNDLE_INSTALL_CONTEXT_USER, REG_KEY_DEFAULT, &dwRelatedBundleIndex, sczRelatedBundleId);
TestThrowOnFailure(hr, L"Failed to enumerate related bundle.");
NativeAssert::StringEqual(TEST_BUNDLE_ID, sczRelatedBundleId);
// Verify we can read the bundle variables via the API
hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
NativeAssert::StringEqual(L"42", sczValue);
// end session
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
TestThrowOnFailure(hr, L"Failed to unregister bundle.");
}
finally
{
ReleaseStr(sczRelatedBundleId);
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 ResumeTest()
{
HRESULT hr = S_OK;
IXMLDOMElement* pixeBundle = NULL;
LPWSTR sczCurrentProcess = NULL;
LPWSTR sczValue = NULL;
BURN_VARIABLES variables = { };
BURN_USER_EXPERIENCE userExperience = { };
BOOTSTRAPPER_COMMAND command = { };
BURN_REGISTRATION registration = { };
BURN_LOGGING logging = { };
BURN_PACKAGES packages = { };
BURN_PLAN plan = { };
BURN_CACHE cache = { };
BURN_ENGINE_COMMAND internalCommand = { };
BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
BYTE* pbBuffer = NULL;
SIZE_T cbBuffer = 0;
SIZE_T piBuffer = 0;
String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
DWORD dwRegistrationOptions = 0;
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" "
L" "
L" "
L" "
L"";
// load XML document
LoadBundleXmlHelper(wzDocument, &pixeBundle);
hr = CacheInitialize(&cache, &internalCommand);
TestThrowOnFailure(hr, L"Failed initialize cache.");
hr = VariableInitialize(&variables);
TestThrowOnFailure(hr, L"Failed to initialize variables.");
hr = VariablesParseFromXml(&variables, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse variables from XML.");
hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
hr = RegistrationParseFromXml(®istration, &cache, pixeBundle);
TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
plan.action = BOOTSTRAPPER_ACTION_INSTALL;
plan.pCommand = &command;
plan.pInternalCommand = &internalCommand;
hr = PlanSetResumeCommand(&plan, ®istration, &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, &cache, &variables, dwRegistrationOptions, 0, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
TestThrowOnFailure(hr, L"Failed to register bundle.");
VariableSetNumericHelper(&variables, L"MyBurnVariable1", 42);
VariableSetStringHelper(&variables, L"MyBurnVariable2", L"bar", FALSE);
VariableSetVersionHelper(&variables, L"MyBurnVariable3", L"v1.0-beta");
hr = VariableSerialize(&variables, TRUE, &pbBuffer, &cbBuffer);
TestThrowOnFailure(hr, "Failed to serialize variables.");
if (!Directory::Exists(cacheDirectory))
{
Directory::CreateDirectory(cacheDirectory);
}
hr = RegistrationSaveState(®istration, pbBuffer, cbBuffer);
TestThrowOnFailure(hr, L"Failed to save state.");
ReleaseNullBuffer(pbBuffer);
cbBuffer = 0;
// Verify the variables exist
Assert::Equal(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
Assert::Equal(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
Assert::Equal(gcnew String(L"1.0-beta"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable3"), nullptr));
Assert::Empty((System::Collections::IEnumerable^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"WixBundleForcedRestartPackage"), nullptr));
hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
NativeAssert::StringEqual(L"42", sczValue);
// 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, &cache, &variables, &packages, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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(TEST_BUNDLE_ID), 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.");
hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &piBuffer);
TestThrowOnFailure(hr, L"Failed to deserialize variables.");
// write active resume mode
hr = RegistrationSessionResume(®istration, &variables, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
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(TEST_BUNDLE_ID), nullptr));
// end session
hr = RegistrationSessionEnd(®istration, &cache, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
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, // 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;
}