aboutsummaryrefslogtreecommitdiff
path: root/src/burn/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/test')
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj1
-rw-r--r--src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters3
-rw-r--r--src/burn/test/BurnUnitTest/ExitCodeTest.cpp361
-rw-r--r--src/burn/test/BurnUnitTest/precomp.h1
4 files changed, 366 insertions, 0 deletions
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
index 04491608..fe86f042 100644
--- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj
@@ -48,6 +48,7 @@
48 <ClCompile Include="CacheTest.cpp" /> 48 <ClCompile Include="CacheTest.cpp" />
49 <ClCompile Include="ElevationTest.cpp" /> 49 <ClCompile Include="ElevationTest.cpp" />
50 <ClCompile Include="EmbeddedTest.cpp" /> 50 <ClCompile Include="EmbeddedTest.cpp" />
51 <ClCompile Include="ExitCodeTest.cpp" />
51 <ClCompile Include="LoggingTest.cpp" /> 52 <ClCompile Include="LoggingTest.cpp" />
52 <ClCompile Include="ManifestHelpers.cpp" /> 53 <ClCompile Include="ManifestHelpers.cpp" />
53 <ClCompile Include="ManifestTest.cpp" /> 54 <ClCompile Include="ManifestTest.cpp" />
diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
index 19061009..82725436 100644
--- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
+++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
@@ -27,6 +27,9 @@
27 <ClCompile Include="EmbeddedTest.cpp"> 27 <ClCompile Include="EmbeddedTest.cpp">
28 <Filter>Source Files</Filter> 28 <Filter>Source Files</Filter>
29 </ClCompile> 29 </ClCompile>
30 <ClCompile Include="ExitCodeTest.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
30 <ClCompile Include="LoggingTest.cpp"> 33 <ClCompile Include="LoggingTest.cpp">
31 <Filter>Source Files</Filter> 34 <Filter>Source Files</Filter>
32 </ClCompile> 35 </ClCompile>
diff --git a/src/burn/test/BurnUnitTest/ExitCodeTest.cpp b/src/burn/test/BurnUnitTest/ExitCodeTest.cpp
new file mode 100644
index 00000000..2c32c6ab
--- /dev/null
+++ b/src/burn/test/BurnUnitTest/ExitCodeTest.cpp
@@ -0,0 +1,361 @@
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#include "precomp.h"
4
5
6namespace Microsoft
7{
8namespace Tools
9{
10namespace WindowsInstallerXml
11{
12namespace Test
13{
14namespace Bootstrapper
15{
16 using namespace System;
17 using namespace System::IO;
18 using namespace System::Threading;
19 using namespace Xunit;
20
21struct EXIT_CODE_ITEM
22{
23 DWORD dwExitCode;
24 HRESULT hrExpectedResult;
25 BOOTSTRAPPER_APPLY_RESTART expectedRestart;
26 LPCWSTR wzPackageId;
27 HRESULT hrResultPerUser;
28 BOOTSTRAPPER_APPLY_RESTART restartPerUser;
29 HRESULT hrResultPerMachine;
30 BOOTSTRAPPER_APPLY_RESTART restartPerMachine;
31};
32
33static BOOL STDAPICALLTYPE ExitCodeTest_ShellExecuteExW(
34 __inout LPSHELLEXECUTEINFOW lpExecInfo
35 );
36static DWORD CALLBACK ExitCodeTest_ElevationThreadProc(
37 __in LPVOID lpThreadParameter
38 );
39static BOOL STDAPICALLTYPE ExitCodeTest_CreateProcessW(
40 __in_opt LPCWSTR lpApplicationName,
41 __inout_opt LPWSTR lpCommandLine,
42 __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
43 __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
44 __in BOOL bInheritHandles,
45 __in DWORD dwCreationFlags,
46 __in_opt LPVOID lpEnvironment,
47 __in_opt LPCWSTR lpCurrentDirectory,
48 __in LPSTARTUPINFOW lpStartupInfo,
49 __out LPPROCESS_INFORMATION lpProcessInformation
50 );
51static DWORD CALLBACK ExitCodeTest_PackageThreadProc(
52 __in LPVOID lpThreadParameter
53 );
54static int ExitCodeTest_GenericMessageHandler(
55 __in GENERIC_EXECUTE_MESSAGE* pMessage,
56 __in LPVOID pvContext
57 );
58static void LoadEngineState(
59 __in BURN_ENGINE_STATE* pEngineState
60 );
61
62 public ref class ExitCodeTest : BurnUnitTest
63 {
64 public:
65 ExitCodeTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
66 {
67 }
68
69 [Fact]
70 void ExitCodeHandlingTest()
71 {
72 HRESULT hr = S_OK;
73 BURN_ENGINE_STATE engineState = { };
74 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection;
75 EXIT_CODE_ITEM rgExitCodeItems[] =
76 {
77 { 0, S_OK, BOOTSTRAPPER_APPLY_RESTART_NONE, L"Standard" },
78 { 1, HRESULT_FROM_WIN32(1), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Standard" },
79 { ERROR_SUCCESS_REBOOT_REQUIRED, S_OK, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, L"Standard" },
80 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), S_OK, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, L"Standard" },
81 { ERROR_SUCCESS_RESTART_REQUIRED, S_OK, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, L"Standard" },
82 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED), S_OK, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, L"Standard" },
83 { ERROR_SUCCESS_REBOOT_INITIATED, S_OK, BOOTSTRAPPER_APPLY_RESTART_INITIATED, L"Standard" },
84 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED), S_OK, BOOTSTRAPPER_APPLY_RESTART_INITIATED, L"Standard" },
85 { 0, E_FAIL, BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
86 { 1, S_OK, BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
87 { 3, S_OK, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, L"Custom" },
88 { 4, S_OK, BOOTSTRAPPER_APPLY_RESTART_INITIATED, L"Custom" },
89 { ERROR_SUCCESS_REBOOT_REQUIRED, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
90 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
91 { ERROR_SUCCESS_RESTART_REQUIRED, HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
92 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED), HRESULT_FROM_WIN32(ERROR_SUCCESS_RESTART_REQUIRED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
93 { ERROR_SUCCESS_REBOOT_INITIATED, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
94 { (DWORD)HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED), HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED), BOOTSTRAPPER_APPLY_RESTART_NONE, L"Custom" },
95 };
96
97 engineState.sczBundleEngineWorkingPath = L"tests\\ignore\\this\\path\\to\\burn.exe";
98
99 try
100 {
101 ShelFunctionOverride(ExitCodeTest_ShellExecuteExW);
102 CoreFunctionOverride(ExitCodeTest_CreateProcessW, ThrdWaitForCompletion);
103
104 //
105 // per-user side setup
106 //
107 LoadEngineState(&engineState);
108
109 hr = ElevationElevate(&engineState, NULL);
110 TestThrowOnFailure(hr, L"Failed to elevate.");
111
112 for (DWORD i = 0; i < countof(rgExitCodeItems); ++i)
113 {
114 // "run" the package both per-user and per-machine
115 ExecuteExePackage(&engineState, rgExitCodeItems + i);
116 }
117
118 //
119 // initiate termination
120 //
121 hr = PipeTerminateChildProcess(pConnection, 0, FALSE);
122 TestThrowOnFailure(hr, L"Failed to terminate elevated process.");
123
124 // check results
125 for (DWORD i = 0; i < countof(rgExitCodeItems); ++i)
126 {
127 EXIT_CODE_ITEM* pExitCode = rgExitCodeItems + i;
128 String^ packageId = gcnew String(pExitCode->wzPackageId);
129 String^ exitCodeString = ((UInt32)pExitCode->dwExitCode).ToString();
130
131 NativeAssert::SpecificReturnCode(pExitCode->hrExpectedResult, pExitCode->hrResultPerMachine, L"Per-machine package: {0}, exit code: {1}", packageId, exitCodeString);
132 Assert::True(pExitCode->expectedRestart == pExitCode->restartPerMachine, String::Format("Per-machine package: {0}, exit code: {1}, expected restart type '{2}' but got '{3}'", packageId, exitCodeString, gcnew String(LoggingRestartToString(pExitCode->expectedRestart)), gcnew String(LoggingRestartToString(pExitCode->restartPerMachine))));
133
134 NativeAssert::SpecificReturnCode(pExitCode->hrExpectedResult, pExitCode->hrResultPerUser, L"Per-user package: {0}, exit code: {1}", packageId, exitCodeString);
135 Assert::True(pExitCode->expectedRestart == pExitCode->restartPerUser, String::Format("Per-user package: {0}, exit code: {1}, expected restart type '{2}' but got '{3}'", packageId, exitCodeString, gcnew String(LoggingRestartToString(pExitCode->expectedRestart)), gcnew String(LoggingRestartToString(pExitCode->restartPerUser))));
136 }
137 }
138 finally
139 {
140 VariablesUninitialize(&engineState.variables);
141 PipeConnectionUninitialize(pConnection);
142 }
143 }
144
145 private:
146 void ExecuteExePackage(
147 __in BURN_ENGINE_STATE* pEngineState,
148 __in EXIT_CODE_ITEM* pExitCode
149 )
150 {
151 HRESULT hr = S_OK;
152 LPWSTR sczExitCode = NULL;
153 BURN_PACKAGE* pPackage = NULL;
154 BURN_EXECUTE_ACTION executeAction = { };
155 BURN_PIPE_CONNECTION* pConnection = &pEngineState->companionConnection;
156 BOOL fRollback = FALSE;
157
158 hr = PackageFindById(&pEngineState->packages, pExitCode->wzPackageId, &pPackage);
159 TestThrowOnFailure(hr, L"Failed to find package.");
160
161 executeAction.type = BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE;
162 executeAction.exePackage.action = BOOTSTRAPPER_ACTION_STATE_INSTALL;
163 executeAction.exePackage.pPackage = pPackage;
164
165 try
166 {
167 hr = StrAllocFormatted(&sczExitCode, L"%u", pExitCode->dwExitCode);
168 TestThrowOnFailure(hr, L"Failed to convert exit code to string.");
169
170 hr = VariableSetString(&pEngineState->variables, L"ExeExitCode", sczExitCode, FALSE, FALSE);
171 TestThrowOnFailure(hr, L"Failed to set variable.");
172
173 pExitCode->hrResultPerMachine = ElevationExecuteExePackage(pConnection->hPipe, &executeAction, &pEngineState->variables, fRollback, ExitCodeTest_GenericMessageHandler, NULL, &pExitCode->restartPerMachine);
174
175 pExitCode->hrResultPerUser = ExeEngineExecutePackage(&executeAction, &pEngineState->cache, &pEngineState->variables, fRollback, ExitCodeTest_GenericMessageHandler, NULL, &pExitCode->restartPerUser);
176 }
177 finally
178 {
179 ReleaseStr(sczExitCode);
180 }
181 }
182 };
183
184
185static BOOL STDAPICALLTYPE ExitCodeTest_ShellExecuteExW(
186 __inout LPSHELLEXECUTEINFOW lpExecInfo
187 )
188{
189 HRESULT hr = S_OK;
190 LPWSTR scz = NULL;
191
192 hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0);
193 ExitOnFailure(hr, "Failed to copy arguments.");
194
195 // Pretend this thread is the elevated process.
196 lpExecInfo->hProcess = ::CreateThread(NULL, 0, ExitCodeTest_ElevationThreadProc, scz, 0, NULL);
197 ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread.");
198 scz = NULL;
199
200LExit:
201 ReleaseStr(scz);
202
203 return SUCCEEDED(hr);
204}
205
206static DWORD CALLBACK ExitCodeTest_ElevationThreadProc(
207 __in LPVOID lpThreadParameter
208 )
209{
210 HRESULT hr = S_OK;
211 LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
212 BURN_ENGINE_STATE engineState = { };
213 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection;
214 HANDLE hLock = NULL;
215 DWORD dwChildExitCode = 0;
216 BOOL fRestart = FALSE;
217 BOOL fApplying = FALSE;
218
219 LoadEngineState(&engineState);
220
221 StrAlloc(&pConnection->sczName, MAX_PATH);
222 StrAlloc(&pConnection->sczSecret, MAX_PATH);
223
224 // parse command line arguments
225 if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", pConnection->sczName, MAX_PATH, pConnection->sczSecret, MAX_PATH, &pConnection->dwProcessId))
226 {
227 hr = E_INVALIDARG;
228 ExitOnFailure(hr, "Failed to parse argument string.");
229 }
230
231 // set up connection with per-user process
232 hr = PipeChildConnect(pConnection, TRUE);
233 ExitOnFailure(hr, "Failed to connect to per-user process.");
234
235 hr = ElevationChildPumpMessages(pConnection->hPipe, pConnection->hCachePipe, &engineState.approvedExes, &engineState.cache, &engineState.containers, &engineState.packages, &engineState.payloads, &engineState.variables, &engineState.registration, &engineState.userExperience, &hLock, &dwChildExitCode, &fRestart, &fApplying);
236 ExitOnFailure(hr, "Failed while pumping messages in child 'process'.");
237
238LExit:
239 PipeConnectionUninitialize(pConnection);
240 VariablesUninitialize(&engineState.variables);
241 ReleaseStr(sczArguments);
242
243 return FAILED(hr) ? (DWORD)hr : dwChildExitCode;
244}
245
246static BOOL STDAPICALLTYPE ExitCodeTest_CreateProcessW(
247 __in_opt LPCWSTR /*lpApplicationName*/,
248 __inout_opt LPWSTR lpCommandLine,
249 __in_opt LPSECURITY_ATTRIBUTES /*lpProcessAttributes*/,
250 __in_opt LPSECURITY_ATTRIBUTES /*lpThreadAttributes*/,
251 __in BOOL /*bInheritHandles*/,
252 __in DWORD /*dwCreationFlags*/,
253 __in_opt LPVOID /*lpEnvironment*/,
254 __in_opt LPCWSTR /*lpCurrentDirectory*/,
255 __in LPSTARTUPINFOW /*lpStartupInfo*/,
256 __out LPPROCESS_INFORMATION lpProcessInformation
257 )
258{
259 HRESULT hr = S_OK;
260 LPWSTR scz = NULL;
261 LPCWSTR wzArgs = lpCommandLine;
262
263 hr = StrAllocString(&scz, wzArgs, 0);
264 ExitOnFailure(hr, "Failed to copy arguments.");
265
266 // Pretend this thread is the package process.
267 lpProcessInformation->hProcess = ::CreateThread(NULL, 0, ExitCodeTest_PackageThreadProc, scz, 0, NULL);
268 ExitOnNullWithLastError(lpProcessInformation->hProcess, hr, "Failed to create thread.");
269
270 scz = NULL;
271
272LExit:
273 ReleaseStr(scz);
274
275 return SUCCEEDED(hr);
276}
277
278static DWORD CALLBACK ExitCodeTest_PackageThreadProc(
279 __in LPVOID lpThreadParameter
280 )
281{
282 HRESULT hr = S_OK;
283 LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
284 int argc = 0;
285 LPWSTR* argv = NULL;
286 DWORD dwResult = 0;
287
288 hr = AppParseCommandLine(sczArguments, &argc, &argv);
289 ExitOnFailure(hr, "Failed to parse command line: %ls", sczArguments);
290
291 hr = StrStringToUInt32(argv[1], 0, reinterpret_cast<UINT*>(&dwResult));
292 ExitOnFailure(hr, "Failed to convert %ls to DWORD.", argv[1]);
293
294LExit:
295 AppFreeCommandLineArgs(argv);
296 ReleaseStr(sczArguments);
297
298 return FAILED(hr) ? (DWORD)hr : dwResult;
299}
300
301static int ExitCodeTest_GenericMessageHandler(
302 __in GENERIC_EXECUTE_MESSAGE* /*pMessage*/,
303 __in LPVOID /*pvContext*/
304 )
305{
306 return IDNOACTION;
307}
308
309static void LoadEngineState(
310 __in BURN_ENGINE_STATE* pEngineState
311 )
312{
313 HRESULT hr = S_OK;
314 IXMLDOMElement* pixeBundle = NULL;
315
316 LPCWSTR wzDocument =
317 L"<BurnManifest>"
318 L" <Payload Id='test.exe' FilePath='test.exe' Packaging='external' SourcePath='test.exe' Hash='000000000000' FileSize='1' />"
319 L" <Chain>"
320 L" <ExePackage Id='Custom' Cache='remove' CacheId='test.exe' InstallSize='1' Size='1' PerMachine='no' Permanent='yes' Vital='yes' DetectCondition='' InstallArguments='[ExeExitCode]' UninstallArguments='' Uninstallable='no' RepairArguments='' Repairable='no' Protocol='none' DetectionType='condition'>"
321 L" <ExitCode Code='0' Type='2' />"
322 L" <ExitCode Code='3' Type='3' />"
323 L" <ExitCode Code='4' Type='4' />"
324 L" <ExitCode Code='3010' Type='2' />"
325 L" <ExitCode Code='-2147021886' Type='2' />"
326 L" <ExitCode Code='3011' Type='2' />"
327 L" <ExitCode Code='-2147021885' Type='2' />"
328 L" <ExitCode Code='1641' Type='2' />"
329 L" <ExitCode Code='-2147023255' Type='2' />"
330 L" <ExitCode Code='*' Type='1' />"
331 L" <PayloadRef Id='test.exe' />"
332 L" </ExePackage>"
333 L" <ExePackage Id='Standard' Cache='remove' CacheId='test.exe' InstallSize='1' Size='1' PerMachine='no' Permanent='yes' Vital='yes' DetectCondition='' InstallArguments='[ExeExitCode]' UninstallArguments='' Uninstallable='no' RepairArguments='' Repairable='no' Protocol='none' DetectionType='condition'>"
334 L" <PayloadRef Id='test.exe' />"
335 L" </ExePackage>"
336 L" </Chain>"
337 L"</BurnManifest>";
338
339 VariableInitialize(&pEngineState->variables);
340
341 PipeConnectionInitialize(&pEngineState->companionConnection);
342
343 hr = CacheInitialize(&pEngineState->cache, &pEngineState->internalCommand);
344 TestThrowOnFailure(hr, "CacheInitialize failed.");
345
346 // load XML document
347 LoadBundleXmlHelper(wzDocument, &pixeBundle);
348
349 hr = PayloadsParseFromXml(&pEngineState->payloads, &pEngineState->containers, &pEngineState->layoutPayloads, pixeBundle);
350 TestThrowOnFailure(hr, "Failed to parse payloads from manifest.");
351
352 hr = PackagesParseFromXml(&pEngineState->packages, &pEngineState->payloads, pixeBundle);
353 TestThrowOnFailure(hr, "Failed to parse packages from manifest.");
354
355 ReleaseObject(pixeBundle);
356}
357}
358}
359}
360}
361}
diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h
index 84e989a4..2b90cb58 100644
--- a/src/burn/test/BurnUnitTest/precomp.h
+++ b/src/burn/test/BurnUnitTest/precomp.h
@@ -13,6 +13,7 @@
13#include "wininet.h" 13#include "wininet.h"
14 14
15#include <dutil.h> 15#include <dutil.h>
16#include <apputil.h>
16#include <verutil.h> 17#include <verutil.h>
17#include <cryputil.h> 18#include <cryputil.h>
18#include <dlutil.h> 19#include <dlutil.h>