From befcd209d62a25020f46a688002b259c59e4dc3b Mon Sep 17 00:00:00 2001 From: Sean Hall <r.sean.hall@gmail.com> Date: Mon, 28 Feb 2022 18:42:51 -0600 Subject: Refactor related bundle enumeration into butil. Related to #3693 --- src/burn/engine/precomp.h | 1 + src/burn/engine/relatedbundle.cpp | 390 +++------------------ src/burn/test/BurnUnitTest/BurnTestException.h | 11 +- src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj | 1 + .../test/BurnUnitTest/BurnUnitTest.vcxproj.filters | 3 + src/burn/test/BurnUnitTest/RelatedBundleTest.cpp | 199 +++++++++++ src/burn/test/BurnUnitTest/precomp.h | 1 + 7 files changed, 261 insertions(+), 345 deletions(-) create mode 100644 src/burn/test/BurnUnitTest/RelatedBundleTest.cpp (limited to 'src/burn') diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h index 26adf44c..c83c1e74 100644 --- a/src/burn/engine/precomp.h +++ b/src/burn/engine/precomp.h @@ -55,6 +55,7 @@ #include <atomutil.h> #include <apuputil.h> #include <dpiutil.h> +#include <butil.h> #include "BootstrapperEngine.h" #include "BootstrapperApplication.h" diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp index 3e0bc799..e6633131 100644 --- a/src/burn/engine/relatedbundle.cpp +++ b/src/burn/engine/relatedbundle.cpp @@ -2,6 +2,12 @@ #include "precomp.h" +typedef struct _BUNDLE_QUERY_CONTEXT +{ + BURN_REGISTRATION* pRegistration; + BURN_RELATED_BUNDLES* pRelatedBundles; +} BUNDLE_QUERY_CONTEXT; + // internal function declarations static __callback int __cdecl CompareRelatedBundles( @@ -9,25 +15,15 @@ static __callback int __cdecl CompareRelatedBundles( __in const void* pvLeft, __in const void* pvRight ); -static HRESULT InitializeForScopeAndBitness( - __in BOOL fPerMachine, - __in REG_KEY_BITNESS regBitness, - __in BURN_REGISTRATION* pRegistration, - __in BURN_RELATED_BUNDLES* pRelatedBundles +static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback( + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle, + __in_opt LPVOID pvContext ); static HRESULT LoadIfRelatedBundle( - __in BOOL fPerMachine, - __in REG_KEY_BITNESS regBitness, - __in HKEY hkUninstallKey, - __in_z LPCWSTR sczRelatedBundleId, + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle, __in BURN_REGISTRATION* pRegistration, __in BURN_RELATED_BUNDLES* pRelatedBundles ); -static HRESULT DetermineRelationType( - __in HKEY hkBundleId, - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType - ); static HRESULT LoadRelatedBundleFromKey( __in_z LPCWSTR wzRelatedBundleId, __in HKEY hkBundleId, @@ -46,12 +42,25 @@ extern "C" HRESULT RelatedBundlesInitializeForScope( ) { HRESULT hr = S_OK; - - hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_32BIT, pRegistration, pRelatedBundles); - ExitOnFailure(hr, "Failed to open 32-bit uninstall registry key."); - - hr = InitializeForScopeAndBitness(fPerMachine, REG_KEY_64BIT, pRegistration, pRelatedBundles); - ExitOnFailure(hr, "Failed to open 64-bit uninstall registry key."); + BUNDLE_INSTALL_CONTEXT installContext = fPerMachine ? BUNDLE_INSTALL_CONTEXT_MACHINE : BUNDLE_INSTALL_CONTEXT_USER; + BUNDLE_QUERY_CONTEXT queryContext = { }; + + queryContext.pRegistration = pRegistration; + queryContext.pRelatedBundles = pRelatedBundles; + + hr = BundleQueryRelatedBundles( + installContext, + const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), + pRegistration->cDetectCodes, + const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), + pRegistration->cUpgradeCodes, + const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), + pRegistration->cAddonCodes, + const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), + pRegistration->cPatchCodes, + QueryRelatedBundlesCallback, + &queryContext); + ExitOnFailure(hr, "Failed to initialize related bundles for scope."); LExit: return hr; @@ -166,346 +175,53 @@ static __callback int __cdecl CompareRelatedBundles( return ret; } -static HRESULT InitializeForScopeAndBitness( - __in BOOL fPerMachine, - __in REG_KEY_BITNESS regBitness, - __in BURN_REGISTRATION * pRegistration, - __in BURN_RELATED_BUNDLES * pRelatedBundles -) +static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback( + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle, + __in_opt LPVOID pvContext + ) { HRESULT hr = S_OK; - HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - HKEY hkUninstallKey = NULL; - LPWSTR sczRelatedBundleId = NULL; + BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE; + BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext); - hr = RegOpenEx(hkRoot, BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, regBitness, &hkUninstallKey); - if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) - { - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to open uninstall registry key."); - - for (DWORD dwIndex = 0; /* exit via break below */; ++dwIndex) - { - hr = RegKeyEnum(hkUninstallKey, dwIndex, &sczRelatedBundleId); - if (E_NOMOREITEMS == hr) - { - hr = S_OK; - break; - } - ExitOnFailure(hr, "Failed to enumerate uninstall key for related bundles."); - - // If we did not find our bundle id, try to load the subkey as a related bundle. - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczRelatedBundleId, -1, pRegistration->sczId, -1)) - { - // Ignore failures here since we'll often find products that aren't actually - // related bundles (or even bundles at all). - HRESULT hrRelatedBundle = LoadIfRelatedBundle(fPerMachine, regBitness, hkUninstallKey, sczRelatedBundleId, pRegistration, pRelatedBundles); - UNREFERENCED_PARAMETER(hrRelatedBundle); - } - } + hr = LoadIfRelatedBundle(pBundle, pContext->pRegistration, pContext->pRelatedBundles); + ExitOnFailure(hr, "Failed to load related bundle: %ls", pBundle->wzBundleId); LExit: - ReleaseStr(sczRelatedBundleId); - ReleaseRegKey(hkUninstallKey); - - return hr; + return result; } static HRESULT LoadIfRelatedBundle( - __in BOOL fPerMachine, - __in REG_KEY_BITNESS regBitness, - __in HKEY hkUninstallKey, - __in_z LPCWSTR sczRelatedBundleId, + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle, __in BURN_REGISTRATION* pRegistration, __in BURN_RELATED_BUNDLES* pRelatedBundles ) { HRESULT hr = S_OK; - HKEY hkBundleId = NULL; - BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_NONE; - - hr = RegOpenEx(hkUninstallKey, sczRelatedBundleId, KEY_READ, regBitness, &hkBundleId); - ExitOnFailure(hr, "Failed to open uninstall key for potential related bundle: %ls", sczRelatedBundleId); - - hr = DetermineRelationType(hkBundleId, pRegistration, &relationType); - if (FAILED(hr) || BOOTSTRAPPER_RELATION_NONE == relationType) - { - // Must not be a related bundle. - hr = E_NOTFOUND; - } - else // load the related bundle. - { - hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); - ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); - - BURN_RELATED_BUNDLE* pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; - - hr = LoadRelatedBundleFromKey(sczRelatedBundleId, hkBundleId, fPerMachine, relationType, pRelatedBundle); - ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", sczRelatedBundleId); - - hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration); - ExitOnFailure(hr, "Failed to detect dependencies for related bundle."); - - ++pRelatedBundles->cRelatedBundles; - } - -LExit: - ReleaseRegKey(hkBundleId); - - return hr; -} - -static HRESULT DetermineRelationType( - __in HKEY hkBundleId, - __in BURN_REGISTRATION* pRegistration, - __out BOOTSTRAPPER_RELATION_TYPE* pRelationType - ) -{ - HRESULT hr = S_OK; - LPWSTR* rgsczUpgradeCodes = NULL; - DWORD cUpgradeCodes = 0; - STRINGDICT_HANDLE sdUpgradeCodes = NULL; - LPWSTR* rgsczAddonCodes = NULL; - DWORD cAddonCodes = 0; - STRINGDICT_HANDLE sdAddonCodes = NULL; - LPWSTR* rgsczDetectCodes = NULL; - DWORD cDetectCodes = 0; - STRINGDICT_HANDLE sdDetectCodes = NULL; - LPWSTR* rgsczPatchCodes = NULL; - DWORD cPatchCodes = 0; - STRINGDICT_HANDLE sdPatchCodes = NULL; - - *pRelationType = BOOTSTRAPPER_RELATION_NONE; - - // All remaining operations should treat all related bundles as non-vital. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes, &cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr) - { - TraceError(hr, "Failed to read upgrade codes as REG_MULTI_SZ. Trying again as REG_SZ in case of older bundles."); - - rgsczUpgradeCodes = reinterpret_cast<LPWSTR*>(MemAlloc(sizeof(LPWSTR), TRUE)); - ExitOnNull(rgsczUpgradeCodes, hr, E_OUTOFMEMORY, "Failed to allocate list for a single upgrade code from older bundle."); - - hr = RegReadString(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczUpgradeCodes[0]); - if (SUCCEEDED(hr)) - { - cUpgradeCodes = 1; - } - } - - // Compare upgrade codes. - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdUpgradeCodes, rgsczUpgradeCodes, cUpgradeCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "upgrade codes"); - - // Upgrade relationship: when their upgrade codes match our upgrade codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for upgrade code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_UPGRADE; - ExitFunction(); - } - - // Detect relationship: when their upgrade codes match our detect codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for detect code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DETECT; - ExitFunction(); - } - - // Dependent relationship: when their upgrade codes match our addon codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - // Dependent relationship: when their upgrade codes match our patch codes. - hr = DictCompareStringListToArray(sdUpgradeCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - ReleaseNullDict(sdUpgradeCodes); - ReleaseNullStrArray(rgsczUpgradeCodes, cUpgradeCodes); - } - - // Compare addon codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, &rgsczAddonCodes, &cAddonCodes); - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdAddonCodes, rgsczAddonCodes, cAddonCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "addon codes"); - - // Addon relationship: when their addon codes match our detect codes. - hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_ADDON; - ExitFunction(); - } - - // Addon relationship: when their addon codes match our upgrade codes. - hr = DictCompareStringListToArray(sdAddonCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_ADDON; - ExitFunction(); - } - - ReleaseNullDict(sdAddonCodes); - ReleaseNullStrArray(rgsczAddonCodes, cAddonCodes); - } + BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext; + BOOTSTRAPPER_RELATION_TYPE relationType = (BOOTSTRAPPER_RELATION_TYPE)pBundle->relationType; + BURN_RELATED_BUNDLE* pRelatedBundle = NULL; - // Compare patch codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, &rgsczPatchCodes, &cPatchCodes); - if (SUCCEEDED(hr)) + // If we found our bundle id, it's not a related bundle. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pRegistration->sczId, -1)) { - hr = DictCreateStringListFromArray(&sdPatchCodes, rgsczPatchCodes, cPatchCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "patch codes"); - - // Patch relationship: when their patch codes match our detect codes. - hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for patch code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_PATCH; - ExitFunction(); - } - - // Patch relationship: when their patch codes match our upgrade codes. - hr = DictCompareStringListToArray(sdPatchCodes, const_cast<LPCWSTR*>(pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for patch code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_PATCH; - ExitFunction(); - } - - ReleaseNullDict(sdPatchCodes); - ReleaseNullStrArray(rgsczPatchCodes, cPatchCodes); + ExitFunction1(hr = S_FALSE); } - // Compare detect codes. - hr = RegReadStringArray(hkBundleId, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, &rgsczDetectCodes, &cDetectCodes); - if (SUCCEEDED(hr)) - { - hr = DictCreateStringListFromArray(&sdDetectCodes, rgsczDetectCodes, cDetectCodes, DICT_FLAG_CASEINSENSITIVE); - ExitOnFailure(hr, "Failed to create string dictionary for %hs.", "detect codes"); + hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRelatedBundles->rgRelatedBundles), pRelatedBundles->cRelatedBundles + 1, sizeof(BURN_RELATED_BUNDLE), 5); + ExitOnFailure(hr, "Failed to ensure there is space for related bundles."); - // Detect relationship: when their detect codes match our detect codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for detect code match."); + pRelatedBundle = pRelatedBundles->rgRelatedBundles + pRelatedBundles->cRelatedBundles; - *pRelationType = BOOTSTRAPPER_RELATION_DETECT; - ExitFunction(); - } + hr = LoadRelatedBundleFromKey(pBundle->wzBundleId, pBundle->hkBundle, fPerMachine, relationType, pRelatedBundle); + ExitOnFailure(hr, "Failed to initialize package from related bundle id: %ls", pBundle->wzBundleId); - // Dependent relationship: when their detect codes match our addon codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); - - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - // Dependent relationship: when their detect codes match our patch codes. - hr = DictCompareStringListToArray(sdDetectCodes, const_cast<LPCWSTR*>(pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes); - if (HRESULT_FROM_WIN32(ERROR_NO_MATCH) == hr) - { - hr = S_OK; - } - else - { - ExitOnFailure(hr, "Failed to do array search for addon code match."); + hr = DependencyDetectRelatedBundle(pRelatedBundle, pRegistration); + ExitOnFailure(hr, "Failed to detect dependencies for related bundle."); - *pRelationType = BOOTSTRAPPER_RELATION_DEPENDENT; - ExitFunction(); - } - - ReleaseNullDict(sdDetectCodes); - ReleaseNullStrArray(rgsczDetectCodes, cDetectCodes); - } + ++pRelatedBundles->cRelatedBundles; LExit: - if (SUCCEEDED(hr) && BOOTSTRAPPER_RELATION_NONE == *pRelationType) - { - hr = E_NOTFOUND; - } - - ReleaseDict(sdUpgradeCodes); - ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); - ReleaseDict(sdAddonCodes); - ReleaseStrArray(rgsczAddonCodes, cAddonCodes); - ReleaseDict(sdDetectCodes); - ReleaseStrArray(rgsczDetectCodes, cDetectCodes); - ReleaseDict(sdPatchCodes); - ReleaseStrArray(rgsczPatchCodes, cPatchCodes); - return hr; } diff --git a/src/burn/test/BurnUnitTest/BurnTestException.h b/src/burn/test/BurnUnitTest/BurnTestException.h index bd94b4fc..e813f95c 100644 --- a/src/burn/test/BurnUnitTest/BurnTestException.h +++ b/src/burn/test/BurnUnitTest/BurnTestException.h @@ -13,19 +13,14 @@ namespace Test namespace Bootstrapper { using namespace System; + using namespace WixBuildTools::TestSupport; - public ref struct BurnTestException : public System::Exception + public ref struct BurnTestException : public SucceededException { public: - BurnTestException(HRESULT error) - { - this->HResult = error; - } - BurnTestException(HRESULT error, String^ message) - : Exception(message) + : SucceededException(error, message) { - this->HResult = error; } property Int32 ErrorCode diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj index 36903239..f4f95b7f 100644 --- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -56,6 +56,7 @@ <DisableSpecificWarnings>4564;4691</DisableSpecificWarnings> </ClCompile> <ClCompile Include="RegistrationTest.cpp" /> + <ClCompile Include="RelatedBundleTest.cpp" /> <ClCompile Include="SearchTest.cpp" /> <ClCompile Include="TestRegistryFixture.cpp" /> <ClCompile Include="VariableHelpers.cpp" /> diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters index 96563fc6..90290f52 100644 --- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters @@ -39,6 +39,9 @@ <ClCompile Include="RegistrationTest.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="RelatedBundleTest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="SearchTest.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp b/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp new file mode 100644 index 00000000..3d1964c3 --- /dev/null +++ b/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp @@ -0,0 +1,199 @@ +// 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 System::IO; + using namespace Xunit; + using namespace WixBuildTools::TestSupport; + + public ref class RelatedBundleTest : BurnUnitTest, IClassFixture<TestRegistryFixture^> + { + private: + TestRegistryFixture^ testRegistry; + public: + RelatedBundleTest(BurnTestFixture^ fixture, TestRegistryFixture^ registryFixture) : BurnUnitTest(fixture) + { + this->testRegistry = registryFixture; + } + + [Fact] + void RelatedBundleDetectPerMachineTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_REGISTRATION registration = { }; + BURN_RELATED_BUNDLES relatedBundles = { }; + BURN_CACHE cache = { }; + BURN_ENGINE_COMMAND internalCommand = { }; + + try + { + this->testRegistry->SetUp(); + this->RegisterFakeBundles(); + + LPCWSTR wzDocument = + L"<Bundle>" + L" <UX>" + L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />" + L" </UX>" + L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />" + L" <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='yes'>" + L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />" + L" </Registration>" + L"</Bundle>"; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = CacheInitialize(&cache, &internalCommand); + TestThrowOnFailure(hr, L"Failed initialize cache."); + + hr = RegistrationParseFromXml(®istration, &cache, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + RelatedBundlesInitializeForScope(registration.fPerMachine, ®istration, &relatedBundles); + + Assert::Equal(1lu, relatedBundles.cRelatedBundles); + + BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0; + NativeAssert::StringEqual(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", pRelatedBundle->package.sczId); + //{E2355133-384C-4332-9B62-1FA950D707B7} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail. + } + finally + { + ReleaseObject(pixeBundle); + RegistrationUninitialize(®istration); + + this->testRegistry->TearDown(); + } + } + + [Fact] + void RelatedBundleDetectPerUserTest() + { + HRESULT hr = S_OK; + IXMLDOMElement* pixeBundle = NULL; + BURN_REGISTRATION registration = { }; + BURN_RELATED_BUNDLES relatedBundles = { }; + BURN_CACHE cache = { }; + BURN_ENGINE_COMMAND internalCommand = { }; + + try + { + this->testRegistry->SetUp(); + this->RegisterFakeBundles(); + + LPCWSTR wzDocument = + L"<Bundle>" + L" <UX>" + L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' />" + L" </UX>" + L" <RelatedBundle Id='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Action='Upgrade' />" + L" <Registration Id='{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}' Tag='foo' ProviderKey='foo' Version='4.0.0.0' ExecutableName='setup.exe' PerMachine='no'>" + L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='4.0.0.0' />" + L" </Registration>" + L"</Bundle>"; + + // load XML document + LoadBundleXmlHelper(wzDocument, &pixeBundle); + + hr = CacheInitialize(&cache, &internalCommand); + TestThrowOnFailure(hr, L"Failed initialize cache."); + + hr = RegistrationParseFromXml(®istration, &cache, pixeBundle); + TestThrowOnFailure(hr, L"Failed to parse registration from XML."); + + RelatedBundlesInitializeForScope(registration.fPerMachine, ®istration, &relatedBundles); + + Assert::Equal(1lu, relatedBundles.cRelatedBundles); + + BURN_RELATED_BUNDLE* pRelatedBundle = relatedBundles.rgRelatedBundles + 0; + NativeAssert::StringEqual(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", pRelatedBundle->package.sczId); + //{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA} should be missing because it causes an error while processing it. It's important that this doesn't cause initialization to fail. + } + finally + { + ReleaseObject(pixeBundle); + RegistrationUninitialize(®istration); + + this->testRegistry->TearDown(); + } + } + + void RegisterFakeBundles() + { + this->RegisterFakeBundle(L"{D54F896D-1952-43E6-9C67-B5652240618C}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"1.0.0.0", TRUE); + this->RegisterFakeBundle(L"{E2355133-384C-4332-9B62-1FA950D707B7}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"1.1.0.0", TRUE); + this->RegisterFakeBundle(L"{AD75BE46-B5D7-4208-BC8B-918553C72D83}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"2.0.0.0", TRUE); + this->RegisterFakeBundle(L"{6DB5D48C-CD7D-40D2-BCBC-AF630E136761}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"3.0.0.0", FALSE); + this->RegisterFakeBundle(L"{42D16EBE-8B6B-4A9A-9AE9-5300F30011AA}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", L"", L"3.1.0.0", FALSE); + this->RegisterFakeBundle(L"{3DB49D3D-1FB8-4147-A465-BBE8BFD0DAD0}", L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}", NULL, L"4.0.0.0", FALSE); + } + + void RegisterFakeBundle(LPCWSTR wzBundleId, LPCWSTR wzUpgradeCodes, LPCWSTR wzCachePath, LPCWSTR wzVersion, BOOL fPerMachine) + { + HRESULT hr = S_OK; + LPWSTR* rgsczUpgradeCodes = NULL; + DWORD cUpgradeCodes = 0; + LPWSTR sczRegistrationKey = NULL; + LPWSTR sczCachePath = NULL; + HKEY hkRegistration = NULL; + HKEY hkRoot = fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + + try + { + hr = StrSplitAllocArray(&rgsczUpgradeCodes, reinterpret_cast<UINT*>(&cUpgradeCodes), wzUpgradeCodes, L";"); + NativeAssert::Succeeded(hr, "Failed to split upgrade codes."); + + hr = StrAllocFormatted(&sczRegistrationKey, L"%s\\%s", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); + NativeAssert::Succeeded(hr, "Failed to build uninstall registry key path."); + + if (!wzCachePath) + { + hr = StrAllocFormatted(&sczCachePath, L"%ls.exe", wzBundleId); + NativeAssert::Succeeded(hr, "Failed to build cache path."); + + wzCachePath = sczCachePath; + } + + hr = RegCreate(hkRoot, sczRegistrationKey, KEY_WRITE, &hkRegistration); + NativeAssert::Succeeded(hr, "Failed to create registration key."); + + hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, rgsczUpgradeCodes, cUpgradeCodes); + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); + + if (wzCachePath && *wzCachePath) + { + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, wzCachePath); + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); + } + + hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, wzVersion); + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); + } + finally + { + ReleaseStrArray(rgsczUpgradeCodes, cUpgradeCodes); + ReleaseStr(sczRegistrationKey); + ReleaseStr(sczCachePath); + ReleaseRegKey(hkRegistration); + } + } + }; +} +} +} +} +} diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h index ecab3494..ded9fc2d 100644 --- a/src/burn/test/BurnUnitTest/precomp.h +++ b/src/burn/test/BurnUnitTest/precomp.h @@ -53,6 +53,7 @@ #include "update.h" #include "pseudobundle.h" #include "registration.h" +#include "relatedbundle.h" #include "plan.h" #include "pipe.h" #include "logging.h" -- cgit v1.2.3-55-g6feb