diff options
Diffstat (limited to '')
| -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 | } |
