diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2022-06-14 15:10:07 -0500 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2022-06-14 21:51:14 -0500 |
commit | 98c369a92891244bde76448ae4a2b623b3ab394c (patch) | |
tree | 398105d02a888ba41b426e84965375e491d79bb7 | |
parent | dea657295df261bb0e3e4d620eeae321531e3a11 (diff) | |
download | wix-98c369a92891244bde76448ae4a2b623b3ab394c.tar.gz wix-98c369a92891244bde76448ae4a2b623b3ab394c.tar.bz2 wix-98c369a92891244bde76448ae4a2b623b3ab394c.zip |
Allow BundlePackage to fallback to QuietUninstallString to uninstall.
Partial implementation of 6756
-rw-r--r-- | src/burn/engine/apply.cpp | 2 | ||||
-rw-r--r-- | src/burn/engine/bundlepackageengine.cpp | 123 | ||||
-rw-r--r-- | src/burn/engine/bundlepackageengine.h | 1 | ||||
-rw-r--r-- | src/burn/engine/elevation.cpp | 13 | ||||
-rw-r--r-- | src/burn/engine/exeengine.cpp | 66 | ||||
-rw-r--r-- | src/burn/engine/package.h | 1 | ||||
-rw-r--r-- | src/burn/engine/plan.cpp | 12 | ||||
-rw-r--r-- | src/burn/test/BurnUnitTest/PlanTest.cpp | 123 | ||||
-rw-r--r-- | src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wixproj | 19 | ||||
-rw-r--r-- | src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wxs | 15 | ||||
-rw-r--r-- | src/test/burn/TestData/BundlePackageTests/PackageFail/PackageFail.wixproj | 12 | ||||
-rw-r--r-- | src/test/burn/WixTestTools/BundleVerifier.cs | 11 | ||||
-rw-r--r-- | src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs | 112 |
13 files changed, 449 insertions, 61 deletions
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp index d215cfe5..e5b978f1 100644 --- a/src/burn/engine/apply.cpp +++ b/src/burn/engine/apply.cpp | |||
@@ -2781,7 +2781,7 @@ static HRESULT ExecuteBundlePackage( | |||
2781 | } | 2781 | } |
2782 | else | 2782 | else |
2783 | { | 2783 | { |
2784 | hrExecute = BundlePackageEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, GenericExecuteMessageHandler, pContext, pRestart); | 2784 | hrExecute = BundlePackageEngineExecutePackage(pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, SUCCEEDED(pExecuteAction->bundlePackage.pPackage->hrCacheResult), GenericExecuteMessageHandler, pContext, pRestart); |
2785 | ExitOnFailure(hrExecute, "Failed to configure per-user BUNDLE package."); | 2785 | ExitOnFailure(hrExecute, "Failed to configure per-user BUNDLE package."); |
2786 | } | 2786 | } |
2787 | 2787 | ||
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp index 81279cf6..7ae12a1e 100644 --- a/src/burn/engine/bundlepackageengine.cpp +++ b/src/burn/engine/bundlepackageengine.cpp | |||
@@ -18,6 +18,7 @@ static HRESULT ExecuteBundle( | |||
18 | __in BURN_CACHE* pCache, | 18 | __in BURN_CACHE* pCache, |
19 | __in BURN_VARIABLES* pVariables, | 19 | __in BURN_VARIABLES* pVariables, |
20 | __in BOOL fRollback, | 20 | __in BOOL fRollback, |
21 | __in BOOL fCacheAvailable, | ||
21 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | 22 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
22 | __in LPVOID pvContext, | 23 | __in LPVOID pvContext, |
23 | __in BOOTSTRAPPER_ACTION_STATE action, | 24 | __in BOOTSTRAPPER_ACTION_STATE action, |
@@ -30,6 +31,11 @@ static HRESULT ExecuteBundle( | |||
30 | __in_z_opt LPCWSTR wzEngineWorkingDirectory, | 31 | __in_z_opt LPCWSTR wzEngineWorkingDirectory, |
31 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | 32 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart |
32 | ); | 33 | ); |
34 | static HRESULT DetectArpEntry( | ||
35 | __in BURN_PACKAGE* pPackage, | ||
36 | __out BOOL* pfRegistered, | ||
37 | __out LPWSTR* psczQuietUninstallString | ||
38 | ); | ||
33 | static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( | 39 | static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( |
34 | __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType | 40 | __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType |
35 | ); | 41 | ); |
@@ -197,6 +203,7 @@ extern "C" void BundlePackageEnginePackageUninitialize( | |||
197 | ) | 203 | ) |
198 | { | 204 | { |
199 | ReleaseStr(pPackage->Bundle.sczBundleId); | 205 | ReleaseStr(pPackage->Bundle.sczBundleId); |
206 | ReleaseStr(pPackage->Bundle.sczArpKeyPath); | ||
200 | ReleaseVerutilVersion(pPackage->Bundle.pVersion); | 207 | ReleaseVerutilVersion(pPackage->Bundle.pVersion); |
201 | ReleaseStr(pPackage->Bundle.sczRegistrationKey); | 208 | ReleaseStr(pPackage->Bundle.sczRegistrationKey); |
202 | ReleaseStr(pPackage->Bundle.sczInstallArguments); | 209 | ReleaseStr(pPackage->Bundle.sczInstallArguments); |
@@ -600,6 +607,7 @@ extern "C" HRESULT BundlePackageEngineExecutePackage( | |||
600 | __in BURN_CACHE* pCache, | 607 | __in BURN_CACHE* pCache, |
601 | __in BURN_VARIABLES* pVariables, | 608 | __in BURN_VARIABLES* pVariables, |
602 | __in BOOL fRollback, | 609 | __in BOOL fRollback, |
610 | __in BOOL fCacheAvailable, | ||
603 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | 611 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
604 | __in LPVOID pvContext, | 612 | __in LPVOID pvContext, |
605 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | 613 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart |
@@ -613,7 +621,7 @@ extern "C" HRESULT BundlePackageEngineExecutePackage( | |||
613 | BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE; | 621 | BOOTSTRAPPER_RELATION_TYPE relationType = BOOTSTRAPPER_RELATION_CHAIN_PACKAGE; |
614 | BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage; | 622 | BURN_PACKAGE* pPackage = pExecuteAction->bundlePackage.pPackage; |
615 | 623 | ||
616 | return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, FALSE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); | 624 | return ExecuteBundle(pCache, pVariables, fRollback, fCacheAvailable, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, FALSE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); |
617 | } | 625 | } |
618 | 626 | ||
619 | extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( | 627 | extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( |
@@ -635,7 +643,7 @@ extern "C" HRESULT BundlePackageEngineExecuteRelatedBundle( | |||
635 | BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType); | 643 | BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pRelatedBundle->planRelationType); |
636 | BURN_PACKAGE* pPackage = &pRelatedBundle->package; | 644 | BURN_PACKAGE* pPackage = &pRelatedBundle->package; |
637 | 645 | ||
638 | return ExecuteBundle(pCache, pVariables, fRollback, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, TRUE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); | 646 | return ExecuteBundle(pCache, pVariables, fRollback, TRUE, pfnGenericMessageHandler, pvContext, action, relationType, pPackage, TRUE, wzParent, wzIgnoreDependencies, wzAncestors, wzEngineWorkingDirectory, pRestart); |
639 | } | 647 | } |
640 | 648 | ||
641 | extern "C" void BundlePackageEngineUpdateInstallRegistrationState( | 649 | extern "C" void BundlePackageEngineUpdateInstallRegistrationState( |
@@ -678,10 +686,10 @@ static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback( | |||
678 | BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType); | 686 | BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType); |
679 | BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext; | 687 | BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext; |
680 | 688 | ||
681 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pPackage->Bundle.sczBundleId, -1)) | 689 | if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pPackage->Bundle.sczBundleId, -1) && |
690 | pPackage->Bundle.fWin64 == (REG_KEY_64BIT == pBundle->regBitness)) | ||
682 | { | 691 | { |
683 | Assert(BOOTSTRAPPER_RELATION_UPGRADE == relationType); | 692 | Assert(BOOTSTRAPPER_RELATION_UPGRADE == relationType); |
684 | Assert(pPackage->Bundle.fWin64 == (REG_KEY_64BIT == pBundle->regBitness)); | ||
685 | 693 | ||
686 | pContext->fSelfFound = TRUE; | 694 | pContext->fSelfFound = TRUE; |
687 | } | 695 | } |
@@ -727,6 +735,7 @@ static HRESULT ExecuteBundle( | |||
727 | __in BURN_CACHE* pCache, | 735 | __in BURN_CACHE* pCache, |
728 | __in BURN_VARIABLES* pVariables, | 736 | __in BURN_VARIABLES* pVariables, |
729 | __in BOOL fRollback, | 737 | __in BOOL fRollback, |
738 | __in BOOL fCacheAvailable, | ||
730 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | 739 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
731 | __in LPVOID pvContext, | 740 | __in LPVOID pvContext, |
732 | __in BOOTSTRAPPER_ACTION_STATE action, | 741 | __in BOOTSTRAPPER_ACTION_STATE action, |
@@ -749,6 +758,10 @@ static HRESULT ExecuteBundle( | |||
749 | LPWSTR sczUserArgs = NULL; | 758 | LPWSTR sczUserArgs = NULL; |
750 | LPWSTR sczUserArgsObfuscated = NULL; | 759 | LPWSTR sczUserArgsObfuscated = NULL; |
751 | LPWSTR sczCommandObfuscated = NULL; | 760 | LPWSTR sczCommandObfuscated = NULL; |
761 | LPWSTR sczArpUninstallString = NULL; | ||
762 | int argcArp = 0; | ||
763 | LPWSTR* argvArp = NULL; | ||
764 | BOOL fRegistered = FALSE; | ||
752 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; | 765 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; |
753 | STARTUPINFOW si = { }; | 766 | STARTUPINFOW si = { }; |
754 | PROCESS_INFORMATION pi = { }; | 767 | PROCESS_INFORMATION pi = { }; |
@@ -772,6 +785,51 @@ static HRESULT ExecuteBundle( | |||
772 | hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); | 785 | hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); |
773 | ExitOnFailure(hr, "Failed to get cached path for related bundle: %ls", pPackage->sczId); | 786 | ExitOnFailure(hr, "Failed to get cached path for related bundle: %ls", pPackage->sczId); |
774 | } | 787 | } |
788 | else if (!fCacheAvailable) | ||
789 | { | ||
790 | ExitOnNull(BOOTSTRAPPER_ACTION_STATE_UNINSTALL == action, hr, E_INVALIDARG, "The only supported action when the cache is not available is UNINSTALL."); | ||
791 | |||
792 | hr = DetectArpEntry(pPackage, &fRegistered, &sczArpUninstallString); | ||
793 | ExitOnFailure(hr, "Failed to query ARP for uninstall."); | ||
794 | |||
795 | if (!fRegistered) | ||
796 | { | ||
797 | if (fRollback) | ||
798 | { | ||
799 | LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); | ||
800 | } | ||
801 | else | ||
802 | { | ||
803 | LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId); | ||
804 | } | ||
805 | |||
806 | ExitFunction(); | ||
807 | } | ||
808 | |||
809 | ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "QuietUninstallString is null."); | ||
810 | |||
811 | hr = AppParseCommandLine(sczArpUninstallString, &argcArp, &argvArp); | ||
812 | ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString); | ||
813 | |||
814 | ExitOnNull(argcArp, hr, E_INVALIDARG, "QuietUninstallString must contain an executable path."); | ||
815 | |||
816 | hr = StrAllocString(&sczExecutablePath, argvArp[0], 0); | ||
817 | ExitOnFailure(hr, "Failed to copy executable path."); | ||
818 | |||
819 | if (pPackage->fPerMachine) | ||
820 | { | ||
821 | hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath); | ||
822 | ExitOnFailure(hr, "Failed to verify the QuietUninstallString executable path is in a secure location: %ls", sczExecutablePath); | ||
823 | if (S_FALSE == hr) | ||
824 | { | ||
825 | LogStringLine(REPORT_STANDARD, "The QuietUninstallString executable path is not in a secure location: %ls", sczExecutablePath); | ||
826 | ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)); | ||
827 | } | ||
828 | } | ||
829 | |||
830 | hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); | ||
831 | ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath); | ||
832 | } | ||
775 | else | 833 | else |
776 | { | 834 | { |
777 | // get cached executable path | 835 | // get cached executable path |
@@ -859,6 +917,12 @@ static HRESULT ExecuteBundle( | |||
859 | hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); | 917 | hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); |
860 | ExitOnFailure(hr, "Failed to allocate base command."); | 918 | ExitOnFailure(hr, "Failed to allocate base command."); |
861 | 919 | ||
920 | for (int i = 1; i < argcArp; ++i) | ||
921 | { | ||
922 | hr = AppAppendCommandLineArgument(&sczBaseCommand, argvArp[i]); | ||
923 | ExitOnFailure(hr, "Failed to append argument from ARP."); | ||
924 | } | ||
925 | |||
862 | if (!fRunEmbedded) | 926 | if (!fRunEmbedded) |
863 | { | 927 | { |
864 | hr = StrAllocConcat(&sczBaseCommand, L" -quiet", 0); | 928 | hr = StrAllocConcat(&sczBaseCommand, L" -quiet", 0); |
@@ -962,6 +1026,12 @@ LExit: | |||
962 | StrSecureZeroFreeString(sczUserArgs); | 1026 | StrSecureZeroFreeString(sczUserArgs); |
963 | ReleaseStr(sczUserArgsObfuscated); | 1027 | ReleaseStr(sczUserArgsObfuscated); |
964 | ReleaseStr(sczCommandObfuscated); | 1028 | ReleaseStr(sczCommandObfuscated); |
1029 | ReleaseStr(sczArpUninstallString); | ||
1030 | |||
1031 | if (argvArp) | ||
1032 | { | ||
1033 | AppFreeCommandLineArgs(argvArp); | ||
1034 | } | ||
965 | 1035 | ||
966 | ReleaseHandle(pi.hThread); | 1036 | ReleaseHandle(pi.hThread); |
967 | ReleaseHandle(pi.hProcess); | 1037 | ReleaseHandle(pi.hProcess); |
@@ -974,6 +1044,51 @@ LExit: | |||
974 | return hr; | 1044 | return hr; |
975 | } | 1045 | } |
976 | 1046 | ||
1047 | static HRESULT DetectArpEntry( | ||
1048 | __in BURN_PACKAGE* pPackage, | ||
1049 | __out BOOL* pfRegistered, | ||
1050 | __out LPWSTR* psczQuietUninstallString | ||
1051 | ) | ||
1052 | { | ||
1053 | HRESULT hr = S_OK; | ||
1054 | HKEY hKey = NULL; | ||
1055 | HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | ||
1056 | REG_KEY_BITNESS keyBitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT; | ||
1057 | |||
1058 | *pfRegistered = FALSE; | ||
1059 | if (psczQuietUninstallString) | ||
1060 | { | ||
1061 | ReleaseNullStr(*psczQuietUninstallString); | ||
1062 | } | ||
1063 | |||
1064 | if (!pPackage->Bundle.sczArpKeyPath) | ||
1065 | { | ||
1066 | hr = PathConcatRelativeToBase(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\", pPackage->Bundle.sczBundleId, &pPackage->Bundle.sczArpKeyPath); | ||
1067 | ExitOnFailure(hr, "Failed to build full key path."); | ||
1068 | } | ||
1069 | |||
1070 | hr = RegOpenEx(hkRoot, pPackage->Bundle.sczArpKeyPath, KEY_READ, keyBitness, &hKey); | ||
1071 | if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) | ||
1072 | { | ||
1073 | ExitFunction1(hr = S_OK); | ||
1074 | } | ||
1075 | ExitOnFailure(hr, "Failed to open registry key: %ls.", pPackage->Bundle.sczArpKeyPath); | ||
1076 | |||
1077 | *pfRegistered = TRUE; | ||
1078 | |||
1079 | hr = RegReadString(hKey, L"QuietUninstallString", psczQuietUninstallString); | ||
1080 | if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) | ||
1081 | { | ||
1082 | hr = S_OK; | ||
1083 | } | ||
1084 | ExitOnFailure(hr, "Failed to read QuietUninstallString."); | ||
1085 | |||
1086 | LExit: | ||
1087 | ReleaseRegKey(hKey); | ||
1088 | |||
1089 | return hr; | ||
1090 | } | ||
1091 | |||
977 | static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( | 1092 | static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType( |
978 | __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType | 1093 | __in BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE relationType |
979 | ) | 1094 | ) |
diff --git a/src/burn/engine/bundlepackageengine.h b/src/burn/engine/bundlepackageengine.h index e245f6ce..60854a07 100644 --- a/src/burn/engine/bundlepackageengine.h +++ b/src/burn/engine/bundlepackageengine.h | |||
@@ -53,6 +53,7 @@ HRESULT BundlePackageEngineExecutePackage( | |||
53 | __in BURN_CACHE* pCache, | 53 | __in BURN_CACHE* pCache, |
54 | __in BURN_VARIABLES* pVariables, | 54 | __in BURN_VARIABLES* pVariables, |
55 | __in BOOL fRollback, | 55 | __in BOOL fRollback, |
56 | __in BOOL fCacheAvailable, | ||
56 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | 57 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, |
57 | __in LPVOID pvContext, | 58 | __in LPVOID pvContext, |
58 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | 59 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart |
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index 9c7cf89f..4a5be8ec 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp | |||
@@ -907,6 +907,9 @@ extern "C" HRESULT ElevationExecuteBundlePackage( | |||
907 | hr = BuffWriteNumber(&pbData, &cbData, fRollback); | 907 | hr = BuffWriteNumber(&pbData, &cbData, fRollback); |
908 | ExitOnFailure(hr, "Failed to write rollback."); | 908 | ExitOnFailure(hr, "Failed to write rollback."); |
909 | 909 | ||
910 | hr = BuffWriteNumber(&pbData, &cbData, SUCCEEDED(pExecuteAction->bundlePackage.pPackage->hrCacheResult)); | ||
911 | ExitOnFailure(hr, "Failed to write fCacheAvailable."); | ||
912 | |||
910 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczParent); | 913 | hr = BuffWriteString(&pbData, &cbData, pExecuteAction->bundlePackage.sczParent); |
911 | ExitOnFailure(hr, "Failed to write the parent to the message buffer."); | 914 | ExitOnFailure(hr, "Failed to write the parent to the message buffer."); |
912 | 915 | ||
@@ -2855,7 +2858,8 @@ static HRESULT OnExecuteBundlePackage( | |||
2855 | HRESULT hr = S_OK; | 2858 | HRESULT hr = S_OK; |
2856 | SIZE_T iData = 0; | 2859 | SIZE_T iData = 0; |
2857 | LPWSTR sczPackage = NULL; | 2860 | LPWSTR sczPackage = NULL; |
2858 | DWORD dwRollback = 0; | 2861 | BOOL fRollback = FALSE; |
2862 | BOOL fCacheAvailable = FALSE; | ||
2859 | BURN_EXECUTE_ACTION executeAction = { }; | 2863 | BURN_EXECUTE_ACTION executeAction = { }; |
2860 | LPWSTR sczIgnoreDependencies = NULL; | 2864 | LPWSTR sczIgnoreDependencies = NULL; |
2861 | LPWSTR sczAncestors = NULL; | 2865 | LPWSTR sczAncestors = NULL; |
@@ -2871,9 +2875,12 @@ static HRESULT OnExecuteBundlePackage( | |||
2871 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.bundlePackage.action); | 2875 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&executeAction.bundlePackage.action); |
2872 | ExitOnFailure(hr, "Failed to read action."); | 2876 | ExitOnFailure(hr, "Failed to read action."); |
2873 | 2877 | ||
2874 | hr = BuffReadNumber(pbData, cbData, &iData, &dwRollback); | 2878 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); |
2875 | ExitOnFailure(hr, "Failed to read rollback."); | 2879 | ExitOnFailure(hr, "Failed to read rollback."); |
2876 | 2880 | ||
2881 | hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fCacheAvailable); | ||
2882 | ExitOnFailure(hr, "Failed to read fCacheAvailable."); | ||
2883 | |||
2877 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.bundlePackage.sczParent); | 2884 | hr = BuffReadString(pbData, cbData, &iData, &executeAction.bundlePackage.sczParent); |
2878 | ExitOnFailure(hr, "Failed to read the parent."); | 2885 | ExitOnFailure(hr, "Failed to read the parent."); |
2879 | 2886 | ||
@@ -2918,7 +2925,7 @@ static HRESULT OnExecuteBundlePackage( | |||
2918 | } | 2925 | } |
2919 | 2926 | ||
2920 | // Execute BUNDLE package. | 2927 | // Execute BUNDLE package. |
2921 | hr = BundlePackageEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &bundleRestart); | 2928 | hr = BundlePackageEngineExecutePackage(&executeAction, pCache, pVariables, fRollback, fCacheAvailable, GenericExecuteMessageHandler, hPipe, &bundleRestart); |
2922 | ExitOnFailure(hr, "Failed to execute BUNDLE package."); | 2929 | ExitOnFailure(hr, "Failed to execute BUNDLE package."); |
2923 | 2930 | ||
2924 | LExit: | 2931 | LExit: |
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp index 3a64ecd8..f7be082d 100644 --- a/src/burn/engine/exeengine.cpp +++ b/src/burn/engine/exeengine.cpp | |||
@@ -7,11 +7,6 @@ static HRESULT DetectArpEntry( | |||
7 | __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState, | 7 | __out BOOTSTRAPPER_PACKAGE_STATE* pPackageState, |
8 | __out_opt LPWSTR* psczQuietUninstallString | 8 | __out_opt LPWSTR* psczQuietUninstallString |
9 | ); | 9 | ); |
10 | static HRESULT ParseArpUninstallString( | ||
11 | __in_z LPCWSTR wzArpUninstallString, | ||
12 | __inout LPWSTR* psczExecutablePath, | ||
13 | __inout LPWSTR* psczArguments | ||
14 | ); | ||
15 | 10 | ||
16 | // function definitions | 11 | // function definitions |
17 | 12 | ||
@@ -435,20 +430,20 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
435 | LPWSTR sczUserArgsObfuscated = NULL; | 430 | LPWSTR sczUserArgsObfuscated = NULL; |
436 | LPWSTR sczCommandObfuscated = NULL; | 431 | LPWSTR sczCommandObfuscated = NULL; |
437 | LPWSTR sczArpUninstallString = NULL; | 432 | LPWSTR sczArpUninstallString = NULL; |
438 | LPWSTR sczArpArguments = NULL; | 433 | int argcArp = 0; |
434 | LPWSTR* argvArp = NULL; | ||
439 | BOOTSTRAPPER_PACKAGE_STATE applyState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; | 435 | BOOTSTRAPPER_PACKAGE_STATE applyState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; |
440 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; | 436 | HANDLE hExecutableFile = INVALID_HANDLE_VALUE; |
441 | DWORD dwExitCode = 0; | 437 | DWORD dwExitCode = 0; |
442 | BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; | 438 | BURN_PACKAGE* pPackage = pExecuteAction->exePackage.pPackage; |
443 | BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; | 439 | BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; |
444 | LPCWSTR wzUninstallArguments = pPackage->Exe.sczUninstallArguments; | ||
445 | 440 | ||
446 | if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType && | 441 | if (BURN_EXE_DETECTION_TYPE_ARP == pPackage->Exe.detectionType && |
447 | (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action || | 442 | (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action || |
448 | BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action && fRollback)) | 443 | BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->exePackage.action && fRollback)) |
449 | { | 444 | { |
450 | hr = DetectArpEntry(pPackage, &applyState, &sczArpUninstallString); | 445 | hr = DetectArpEntry(pPackage, &applyState, &sczArpUninstallString); |
451 | ExitOnFailure(hr, "Failed to query ArpEntry for uninstall."); | 446 | ExitOnFailure(hr, "Failed to query ArpEntry for %hs.", BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action ? "uninstall" : "install"); |
452 | 447 | ||
453 | if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == applyState && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action) | 448 | if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == applyState && BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->exePackage.action) |
454 | { | 449 | { |
@@ -487,9 +482,14 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
487 | { | 482 | { |
488 | ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "QuietUninstallString is null."); | 483 | ExitOnNull(sczArpUninstallString, hr, E_INVALIDARG, "QuietUninstallString is null."); |
489 | 484 | ||
490 | hr = ParseArpUninstallString(sczArpUninstallString, &sczExecutablePath, &sczArpArguments); | 485 | hr = AppParseCommandLine(sczArpUninstallString, &argcArp, &argvArp); |
491 | ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString); | 486 | ExitOnFailure(hr, "Failed to parse QuietUninstallString: %ls.", sczArpUninstallString); |
492 | 487 | ||
488 | ExitOnNull(argcArp, hr, E_INVALIDARG, "QuietUninstallString must contain an executable path."); | ||
489 | |||
490 | hr = StrAllocString(&sczExecutablePath, argvArp[0], 0); | ||
491 | ExitOnFailure(hr, "Failed to copy executable path."); | ||
492 | |||
493 | if (pPackage->fPerMachine) | 493 | if (pPackage->fPerMachine) |
494 | { | 494 | { |
495 | hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath); | 495 | hr = ApprovedExesVerifySecureLocation(pCache, pVariables, sczExecutablePath); |
@@ -503,8 +503,6 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
503 | 503 | ||
504 | hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); | 504 | hr = PathGetDirectory(sczExecutablePath, &sczCachedDirectory); |
505 | ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath); | 505 | ExitOnFailure(hr, "Failed to get parent directory for QuietUninstallString executable path: %ls", sczExecutablePath); |
506 | |||
507 | wzUninstallArguments = sczArpArguments; | ||
508 | } | 506 | } |
509 | else | 507 | else |
510 | { | 508 | { |
@@ -528,7 +526,7 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
528 | break; | 526 | break; |
529 | 527 | ||
530 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | 528 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: |
531 | wzArguments = wzUninstallArguments; | 529 | wzArguments = pPackage->Exe.sczUninstallArguments; |
532 | break; | 530 | break; |
533 | 531 | ||
534 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: | 532 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: |
@@ -583,6 +581,12 @@ extern "C" HRESULT ExeEngineExecutePackage( | |||
583 | hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); | 581 | hr = StrAllocFormatted(&sczBaseCommand, L"\"%ls\"", sczExecutablePath); |
584 | ExitOnFailure(hr, "Failed to allocate base command."); | 582 | ExitOnFailure(hr, "Failed to allocate base command."); |
585 | 583 | ||
584 | for (int i = 1; i < argcArp; ++i) | ||
585 | { | ||
586 | hr = AppAppendCommandLineArgument(&sczBaseCommand, argvArp[i]); | ||
587 | ExitOnFailure(hr, "Failed to append argument from ARP."); | ||
588 | } | ||
589 | |||
586 | if (pPackage->Exe.fBundle) | 590 | if (pPackage->Exe.fBundle) |
587 | { | 591 | { |
588 | hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0); | 592 | hr = StrAllocConcat(&sczBaseCommand, L" -norestart", 0); |
@@ -655,7 +659,11 @@ LExit: | |||
655 | ReleaseStr(sczUserArgsObfuscated); | 659 | ReleaseStr(sczUserArgsObfuscated); |
656 | ReleaseStr(sczCommandObfuscated); | 660 | ReleaseStr(sczCommandObfuscated); |
657 | ReleaseStr(sczArpUninstallString); | 661 | ReleaseStr(sczArpUninstallString); |
658 | ReleaseStr(sczArpArguments); | 662 | |
663 | if (argvArp) | ||
664 | { | ||
665 | AppFreeCommandLineArgs(argvArp); | ||
666 | } | ||
659 | 667 | ||
660 | ReleaseFileHandle(hExecutableFile); | 668 | ReleaseFileHandle(hExecutableFile); |
661 | 669 | ||
@@ -1097,35 +1105,3 @@ LExit: | |||
1097 | 1105 | ||
1098 | return hr; | 1106 | return hr; |
1099 | } | 1107 | } |
1100 | |||
1101 | static HRESULT ParseArpUninstallString( | ||
1102 | __in_z LPCWSTR wzArpUninstallString, | ||
1103 | __inout LPWSTR* psczExecutablePath, | ||
1104 | __inout LPWSTR* psczArguments | ||
1105 | ) | ||
1106 | { | ||
1107 | HRESULT hr = S_OK; | ||
1108 | int argc = 0; | ||
1109 | LPWSTR* argv = NULL; | ||
1110 | |||
1111 | hr = AppParseCommandLine(wzArpUninstallString, &argc, &argv); | ||
1112 | ExitOnFailure(hr, "Failed to parse uninstall string as command line: %ls.", wzArpUninstallString); | ||
1113 | ExitOnNull(argc, hr, E_INVALIDARG, "Uninstall string must contain an executable path."); | ||
1114 | |||
1115 | hr = StrAllocString(psczExecutablePath, argv[0], 0); | ||
1116 | ExitOnFailure(hr, "Failed to copy executable path for ArpCommand."); | ||
1117 | |||
1118 | for (int i = 1; i < argc; ++i) | ||
1119 | { | ||
1120 | hr = AppAppendCommandLineArgument(psczArguments, argv[i]); | ||
1121 | ExitOnFailure(hr, "Failed to append argument for ArpCommand."); | ||
1122 | } | ||
1123 | |||
1124 | LExit: | ||
1125 | if (argv) | ||
1126 | { | ||
1127 | AppFreeCommandLineArgs(argv); | ||
1128 | } | ||
1129 | |||
1130 | return hr; | ||
1131 | } | ||
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index 5fccf50a..449c4e08 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h | |||
@@ -321,6 +321,7 @@ typedef struct _BURN_PACKAGE | |||
321 | struct | 321 | struct |
322 | { | 322 | { |
323 | LPWSTR sczBundleId; | 323 | LPWSTR sczBundleId; |
324 | LPWSTR sczArpKeyPath; | ||
324 | VERUTIL_VERSION* pVersion; | 325 | VERUTIL_VERSION* pVersion; |
325 | LPWSTR sczRegistrationKey; | 326 | LPWSTR sczRegistrationKey; |
326 | LPWSTR sczInstallArguments; | 327 | LPWSTR sczInstallArguments; |
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 18f9b274..5c9ebe08 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp | |||
@@ -2805,13 +2805,16 @@ static BURN_CACHE_PACKAGE_TYPE GetCachePackageType( | |||
2805 | case BOOTSTRAPPER_ACTION_STATE_NONE: | 2805 | case BOOTSTRAPPER_ACTION_STATE_NONE: |
2806 | break; | 2806 | break; |
2807 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | 2807 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: |
2808 | if (BURN_PACKAGE_TYPE_BUNDLE == pPackage->type || | 2808 | if (BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_DETECTION_TYPE_ARP != pPackage->Exe.detectionType) |
2809 | BURN_PACKAGE_TYPE_EXE == pPackage->type && BURN_EXE_DETECTION_TYPE_ARP != pPackage->Exe.detectionType) | ||
2810 | { | 2809 | { |
2811 | // Bundle and non-ArpEntry Exe packages require the package for all operations (even uninstall). | 2810 | // non-ArpEntry Exe packages require the package for all operations (even uninstall). |
2812 | // TODO: bundles could theoretically use package cache. | ||
2813 | cachePackageType = BURN_CACHE_PACKAGE_TYPE_REQUIRED; | 2811 | cachePackageType = BURN_CACHE_PACKAGE_TYPE_REQUIRED; |
2814 | } | 2812 | } |
2813 | else if (BURN_PACKAGE_TYPE_BUNDLE == pPackage->type) | ||
2814 | { | ||
2815 | // Bundle packages prefer the cache but can fallback to the ARP registration. | ||
2816 | cachePackageType = BURN_CACHE_PACKAGE_TYPE_OPTIONAL; | ||
2817 | } | ||
2815 | else | 2818 | else |
2816 | { | 2819 | { |
2817 | // The other package types can uninstall without the original package. | 2820 | // The other package types can uninstall without the original package. |
@@ -2823,6 +2826,7 @@ static BURN_CACHE_PACKAGE_TYPE GetCachePackageType( | |||
2823 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; | 2826 | case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; |
2824 | case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; | 2827 | case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; |
2825 | default: | 2828 | default: |
2829 | // TODO: bundles could theoretically use package cache. | ||
2826 | cachePackageType = BURN_CACHE_PACKAGE_TYPE_REQUIRED; | 2830 | cachePackageType = BURN_CACHE_PACKAGE_TYPE_REQUIRED; |
2827 | break; | 2831 | break; |
2828 | } | 2832 | } |
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp index 37027ada..7998f837 100644 --- a/src/burn/test/BurnUnitTest/PlanTest.cpp +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp | |||
@@ -654,10 +654,10 @@ namespace Bootstrapper | |||
654 | fRollback = FALSE; | 654 | fRollback = FALSE; |
655 | dwIndex = 0; | 655 | dwIndex = 0; |
656 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); | 656 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); |
657 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_REQUIRED); | 657 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_OPTIONAL); |
658 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); | 658 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); |
659 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 6); | 659 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 6); |
660 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_REQUIRED); | 660 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_OPTIONAL); |
661 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageB"); | 661 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageB"); |
662 | Assert::Equal(dwIndex, pPlan->cCacheActions); | 662 | Assert::Equal(dwIndex, pPlan->cCacheActions); |
663 | 663 | ||
@@ -825,6 +825,125 @@ namespace Bootstrapper | |||
825 | } | 825 | } |
826 | 826 | ||
827 | [Fact] | 827 | [Fact] |
828 | void MultipleBundlePackageUninstallTest() | ||
829 | { | ||
830 | HRESULT hr = S_OK; | ||
831 | BURN_ENGINE_STATE engineState = { }; | ||
832 | BURN_ENGINE_STATE* pEngineState = &engineState; | ||
833 | BURN_PLAN* pPlan = &engineState.plan; | ||
834 | |||
835 | InitializeEngineStateForCorePlan(wzMultipleBundlePackageManifestFileName, pEngineState); | ||
836 | DetectAttachedContainerAsAttached(pEngineState); | ||
837 | DetectPackagesAsPresentAndCached(pEngineState); | ||
838 | |||
839 | hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL); | ||
840 | NativeAssert::Succeeded(hr, "CorePlan failed"); | ||
841 | |||
842 | Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action); | ||
843 | NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleId); | ||
844 | NativeAssert::StringEqual(L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", pPlan->wzBundleProviderKey); | ||
845 | Assert::Equal<BOOL>(FALSE, pPlan->fEnabledForwardCompatibleBundle); | ||
846 | Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine); | ||
847 | Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); | ||
848 | Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); | ||
849 | Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); | ||
850 | Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade); | ||
851 | Assert::Equal<DWORD>(BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_PROVIDER_KEY, pPlan->dwRegistrationOperations); | ||
852 | |||
853 | BOOL fRollback = FALSE; | ||
854 | DWORD dwIndex = 0; | ||
855 | ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
856 | Assert::Equal(dwIndex, pPlan->cRegistrationActions); | ||
857 | |||
858 | fRollback = TRUE; | ||
859 | dwIndex = 0; | ||
860 | ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
861 | Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions); | ||
862 | |||
863 | fRollback = FALSE; | ||
864 | dwIndex = 0; | ||
865 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); | ||
866 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB", FALSE, BURN_CACHE_PACKAGE_TYPE_OPTIONAL, BURN_CACHE_PACKAGE_TYPE_REQUIRED); | ||
867 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageB"); | ||
868 | ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 6); | ||
869 | ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", FALSE, BURN_CACHE_PACKAGE_TYPE_OPTIONAL, BURN_CACHE_PACKAGE_TYPE_REQUIRED); | ||
870 | ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); | ||
871 | Assert::Equal(dwIndex, pPlan->cCacheActions); | ||
872 | |||
873 | fRollback = TRUE; | ||
874 | dwIndex = 0; | ||
875 | Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); | ||
876 | |||
877 | Assert::Equal(78462280ull, pPlan->qwCacheSizeTotal); | ||
878 | |||
879 | fRollback = FALSE; | ||
880 | dwIndex = 0; | ||
881 | DWORD dwExecuteCheckpointId = 2; | ||
882 | ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); | ||
883 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
884 | ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); | ||
885 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
886 | ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1); | ||
887 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
888 | ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
889 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
890 | dwExecuteCheckpointId += 1; // cache checkpoints | ||
891 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
892 | ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); | ||
893 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
894 | ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", unregisterActions1, 1); | ||
895 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
896 | ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
897 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
898 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
899 | ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); | ||
900 | Assert::Equal(dwIndex, pPlan->cExecuteActions); | ||
901 | |||
902 | fRollback = TRUE; | ||
903 | dwIndex = 0; | ||
904 | dwExecuteCheckpointId = 2; | ||
905 | ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE); | ||
906 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
907 | ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); | ||
908 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
909 | ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageB", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
910 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
911 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
912 | dwExecuteCheckpointId += 1; // cache checkpoints | ||
913 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
914 | ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", registerActions1, 1); | ||
915 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
916 | ValidateExecuteBundlePackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"); | ||
917 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
918 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
919 | ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); | ||
920 | ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); | ||
921 | Assert::Equal(dwIndex, pPlan->cRollbackActions); | ||
922 | |||
923 | Assert::Equal(2ul, pPlan->cExecutePackagesTotal); | ||
924 | Assert::Equal(4ul, pPlan->cOverallProgressTicksTotal); | ||
925 | |||
926 | dwIndex = 0; | ||
927 | Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions); | ||
928 | |||
929 | dwIndex = 0; | ||
930 | ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); | ||
931 | ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); | ||
932 | ValidateCleanAction(pPlan, dwIndex++, L"NetFx48Web"); | ||
933 | Assert::Equal(dwIndex, pPlan->cCleanActions); | ||
934 | |||
935 | UINT uIndex = 0; | ||
936 | ValidatePlannedProvider(pPlan, uIndex++, L"{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}", NULL); | ||
937 | ValidatePlannedProvider(pPlan, uIndex++, L"{7506235A-7C59-4750-82C7-EB460A87ED3A}", NULL); | ||
938 | ValidatePlannedProvider(pPlan, uIndex++, L"{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}", NULL); | ||
939 | Assert::Equal(uIndex, pPlan->cPlannedProviders); | ||
940 | |||
941 | Assert::Equal(3ul, pEngineState->packages.cPackages); | ||
942 | ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); | ||
943 | ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); | ||
944 | } | ||
945 | |||
946 | [Fact] | ||
828 | void OrphanCompatiblePackageTest() | 947 | void OrphanCompatiblePackageTest() |
829 | { | 948 | { |
830 | HRESULT hr = S_OK; | 949 | HRESULT hr = S_OK; |
diff --git a/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wixproj b/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wixproj new file mode 100644 index 00000000..276d9dcb --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wixproj | |||
@@ -0,0 +1,19 @@ | |||
1 | <!-- 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. --> | ||
2 | <Project Sdk="WixToolset.Sdk"> | ||
3 | <PropertyGroup> | ||
4 | <OutputType>Bundle</OutputType> | ||
5 | <UpgradeCode>{1CD57FE5-A2FA-4A72-BEBA-74D9730C2C93}</UpgradeCode> | ||
6 | </PropertyGroup> | ||
7 | <ItemGroup> | ||
8 | <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" /> | ||
9 | </ItemGroup> | ||
10 | <ItemGroup> | ||
11 | <ProjectReference Include="..\PackageFail\PackageFail.wixproj" /> | ||
12 | <ProjectReference Include="..\..\BasicFunctionalityTests\BundleA\BundleA.wixproj" /> | ||
13 | <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" /> | ||
14 | </ItemGroup> | ||
15 | <ItemGroup> | ||
16 | <PackageReference Include="WixToolset.Bal.wixext" /> | ||
17 | <PackageReference Include="WixToolset.NetFx.wixext" /> | ||
18 | </ItemGroup> | ||
19 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wxs b/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wxs new file mode 100644 index 00000000..1e27161c --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/BundlePackageUninstallFailureBundle/BundlePackageUninstallFailureBundle.wxs | |||
@@ -0,0 +1,15 @@ | |||
1 | <!-- 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. --> | ||
2 | |||
3 | |||
4 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"> | ||
5 | <Fragment> | ||
6 | <Variable Name="FAILWHENDEFERRED" bal:Overridable="yes" /> | ||
7 | |||
8 | <PackageGroup Id="BundlePackages"> | ||
9 | <MsiPackage Id="PackageFail" SourceFile="$(var.PackageFail.TargetPath)"> | ||
10 | <MsiProperty Name="WIXFAILWHENDEFERRED" Value="[FAILWHENDEFERRED]" /> | ||
11 | </MsiPackage> | ||
12 | <BundlePackage Id="PackageA" SourceFile="$(var.BundleA.TargetPath)" /> | ||
13 | </PackageGroup> | ||
14 | </Fragment> | ||
15 | </Wix> | ||
diff --git a/src/test/burn/TestData/BundlePackageTests/PackageFail/PackageFail.wixproj b/src/test/burn/TestData/BundlePackageTests/PackageFail/PackageFail.wixproj new file mode 100644 index 00000000..68b5470b --- /dev/null +++ b/src/test/burn/TestData/BundlePackageTests/PackageFail/PackageFail.wixproj | |||
@@ -0,0 +1,12 @@ | |||
1 | <!-- 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. --> | ||
2 | <Project Sdk="WixToolset.Sdk"> | ||
3 | <PropertyGroup> | ||
4 | <UpgradeCode>{2674BFB2-634D-42DB-A1EC-C2CD148A2328}</UpgradeCode> | ||
5 | </PropertyGroup> | ||
6 | <ItemGroup> | ||
7 | <Compile Include="..\..\Templates\PackageFail.wxs" Link="PackageFail.wxs" /> | ||
8 | </ItemGroup> | ||
9 | <ItemGroup> | ||
10 | <PackageReference Include="WixToolset.Util.wixext" /> | ||
11 | </ItemGroup> | ||
12 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs index ff45a291..3a19ca02 100644 --- a/src/test/burn/WixTestTools/BundleVerifier.cs +++ b/src/test/burn/WixTestTools/BundleVerifier.cs | |||
@@ -158,14 +158,21 @@ namespace WixTestTools | |||
158 | } | 158 | } |
159 | } | 159 | } |
160 | 160 | ||
161 | public void VerifyPackageIsCached(string packageId, bool cached = true) | 161 | public string GetPackageEntryPointCachePath(string packageId) |
162 | { | 162 | { |
163 | using var wixOutput = WixOutput.Read(this.BundlePdb); | 163 | using var wixOutput = WixOutput.Read(this.BundlePdb); |
164 | var intermediate = Intermediate.Load(wixOutput); | 164 | var intermediate = Intermediate.Load(wixOutput); |
165 | var section = intermediate.Sections.Single(); | 165 | var section = intermediate.Sections.Single(); |
166 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); | 166 | var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); |
167 | var packagePayloadSymbol = section.Symbols.OfType<WixBundlePayloadSymbol>().Single(p => p.Id.Id == packageSymbol.PayloadRef); | ||
167 | var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); | 168 | var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == true); |
168 | Assert.Equal(cached, Directory.Exists(cachePath)); | 169 | return Path.Combine(cachePath, packagePayloadSymbol.Name); |
170 | } | ||
171 | |||
172 | public void VerifyPackageIsCached(string packageId, bool cached = true) | ||
173 | { | ||
174 | var entryPointCachePath = this.GetPackageEntryPointCachePath(packageId); | ||
175 | Assert.Equal(cached, File.Exists(entryPointCachePath)); | ||
169 | } | 176 | } |
170 | 177 | ||
171 | public void VerifyPackageProviderRemoved(string packageId) | 178 | public void VerifyPackageProviderRemoved(string packageId) |
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs index 0c6e5873..b3ef9430 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs | |||
@@ -5,6 +5,7 @@ namespace WixToolsetTest.BurnE2E | |||
5 | using System; | 5 | using System; |
6 | using System.IO; | 6 | using System.IO; |
7 | using WixTestTools; | 7 | using WixTestTools; |
8 | using WixToolset.Mba.Core; | ||
8 | using Xunit; | 9 | using Xunit; |
9 | using Xunit.Abstractions; | 10 | using Xunit.Abstractions; |
10 | 11 | ||
@@ -137,5 +138,116 @@ namespace WixToolsetTest.BurnE2E | |||
137 | upgradeBundlePackageBundlev1.VerifyUnregisteredAndRemovedFromPackageCache(); | 138 | upgradeBundlePackageBundlev1.VerifyUnregisteredAndRemovedFromPackageCache(); |
138 | bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(); | 139 | bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache(); |
139 | } | 140 | } |
141 | |||
142 | [RuntimeFact] | ||
143 | public void CanRecacheAndReinstallBundlePackageOnUninstallRollback() | ||
144 | { | ||
145 | var packageFail = this.CreatePackageInstaller("PackageFail"); | ||
146 | var packageA = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA"); | ||
147 | var bundleA = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleA"); | ||
148 | var bundlePackageUninstallFailureBundle = this.CreateBundleInstaller("BundlePackageUninstallFailureBundle"); | ||
149 | var testBAController = this.CreateTestBAController(); | ||
150 | var bundleASelfCachedPath = bundleA.GetExpectedCachedBundlePath(); | ||
151 | var bundleAEmbeddedCachedPath = bundlePackageUninstallFailureBundle.GetPackageEntryPointCachePath("PackageA"); | ||
152 | |||
153 | packageA.VerifyInstalled(false); | ||
154 | packageFail.VerifyInstalled(false); | ||
155 | bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
156 | |||
157 | testBAController.SetPackageRequestedCacheType("PackageA", BOOTSTRAPPER_CACHE_TYPE.Remove); | ||
158 | |||
159 | var installLogPath = bundlePackageUninstallFailureBundle.Install(); | ||
160 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
161 | packageFail.VerifyInstalled(true); | ||
162 | bundleA.VerifyRegisteredAndInPackageCache(expectedSystemComponent: 1); | ||
163 | |||
164 | Assert.False(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
165 | Assert.True(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
166 | |||
167 | testBAController.ResetPackageStates("PackageA"); | ||
168 | testBAController.SetAllowAcquireAfterValidationFailure(); | ||
169 | |||
170 | var uninstallLogPath = bundlePackageUninstallFailureBundle.Uninstall((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "FAILWHENDEFERRED=1"); | ||
171 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
172 | bundleA.VerifyRegisteredAndInPackageCache(expectedSystemComponent: 1); | ||
173 | packageFail.VerifyInstalled(true); | ||
174 | |||
175 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, "TESTBA: OnCachePackageNonVitalValidationFailure() - id: PackageA, default: None, requested: Acquire")); | ||
176 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
177 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
178 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
179 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
180 | } | ||
181 | |||
182 | [RuntimeFact] | ||
183 | public void CanReinstallBundlePackageOnUninstallRollback() | ||
184 | { | ||
185 | var packageFail = this.CreatePackageInstaller("PackageFail"); | ||
186 | var packageA = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA"); | ||
187 | var bundleA = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleA"); | ||
188 | var bundlePackageUninstallFailureBundle = this.CreateBundleInstaller("BundlePackageUninstallFailureBundle"); | ||
189 | var bundleASelfCachedPath = bundleA.GetExpectedCachedBundlePath(); | ||
190 | var bundleAEmbeddedCachedPath = bundlePackageUninstallFailureBundle.GetPackageEntryPointCachePath("PackageA"); | ||
191 | |||
192 | packageA.VerifyInstalled(false); | ||
193 | packageFail.VerifyInstalled(false); | ||
194 | bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
195 | |||
196 | var installLogPath = bundlePackageUninstallFailureBundle.Install(); | ||
197 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
198 | packageFail.VerifyInstalled(true); | ||
199 | bundleA.VerifyRegisteredAndInPackageCache(expectedSystemComponent: 1); | ||
200 | |||
201 | Assert.False(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
202 | Assert.True(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
203 | |||
204 | var uninstallLogPath = bundlePackageUninstallFailureBundle.Uninstall((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "FAILWHENDEFERRED=1"); | ||
205 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
206 | bundleA.VerifyRegisteredAndInPackageCache(expectedSystemComponent: 1); | ||
207 | packageFail.VerifyInstalled(true); | ||
208 | |||
209 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, "TESTBA: OnCachePackageNonVitalValidationFailure() - id: PackageA")); | ||
210 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
211 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
212 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
213 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
214 | } | ||
215 | |||
216 | [RuntimeFact] | ||
217 | public void CanSkipReinstallBundlePackageOnUninstallRollback() | ||
218 | { | ||
219 | var packageFail = this.CreatePackageInstaller("PackageFail"); | ||
220 | var packageA = this.CreatePackageInstaller(@"..\BasicFunctionalityTests\PackageA"); | ||
221 | var bundleA = this.CreateBundleInstaller(@"..\BasicFunctionalityTests\BundleA"); | ||
222 | var bundlePackageUninstallFailureBundle = this.CreateBundleInstaller("BundlePackageUninstallFailureBundle"); | ||
223 | var testBAController = this.CreateTestBAController(); | ||
224 | var bundleASelfCachedPath = bundleA.GetExpectedCachedBundlePath(); | ||
225 | var bundleAEmbeddedCachedPath = bundlePackageUninstallFailureBundle.GetPackageEntryPointCachePath("PackageA"); | ||
226 | |||
227 | packageA.VerifyInstalled(false); | ||
228 | packageFail.VerifyInstalled(false); | ||
229 | bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
230 | |||
231 | testBAController.SetPackageRequestedCacheType("PackageA", BOOTSTRAPPER_CACHE_TYPE.Remove); | ||
232 | |||
233 | var installLogPath = bundlePackageUninstallFailureBundle.Install(); | ||
234 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
235 | packageFail.VerifyInstalled(true); | ||
236 | bundleA.VerifyRegisteredAndInPackageCache(expectedSystemComponent: 1); | ||
237 | |||
238 | Assert.False(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
239 | Assert.True(LogVerifier.MessageInLogFile(installLogPath, $"Applying execute package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
240 | |||
241 | var uninstallLogPath = bundlePackageUninstallFailureBundle.Uninstall((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, "FAILWHENDEFERRED=1"); | ||
242 | bundlePackageUninstallFailureBundle.VerifyRegisteredAndInPackageCache(); | ||
243 | bundleA.VerifyUnregisteredAndRemovedFromPackageCache(); | ||
244 | packageFail.VerifyInstalled(true); | ||
245 | |||
246 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, "TESTBA: OnCachePackageNonVitalValidationFailure() - id: PackageA, default: None, requested: None")); | ||
247 | Assert.True(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
248 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying execute package: PackageA, action: Uninstall, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
249 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleASelfCachedPath}"), bundleASelfCachedPath); | ||
250 | Assert.False(LogVerifier.MessageInLogFile(uninstallLogPath, $"Applying rollback package: PackageA, action: Install, path: {bundleAEmbeddedCachedPath}"), bundleAEmbeddedCachedPath); | ||
251 | } | ||
140 | } | 252 | } |
141 | } | 253 | } |