Import BurnUnitTest from old wix4 repo.
Sean Hall committed
Jul 15, 2020 at 21:41 UTC
8bcb8c013a258691d4004c50af4689b90dd05c45
19 files changed
+2928
src/test/BurnUnitTest/AssemblyInfo.cpp
new
+12
@@ -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
+
3
+#include "precomp.h"
4
+
5
+using namespace System::Reflection;
6
+using namespace System::Runtime::CompilerServices;
7
+using namespace System::Runtime::InteropServices;
8
+
9
+[assembly: AssemblyTitleAttribute("Windows Installer XML Burn unit tests")];
10
+[assembly: AssemblyDescriptionAttribute("Burn unit tests")];
11
+[assembly: AssemblyCultureAttribute("")];
12
+[assembly: ComVisible(false)];
src/test/BurnUnitTest/BurnTestException.h
new
+93
@@ -0,0 +1,93 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+ using namespace System;
16
+
17
+ public ref struct BurnTestException : public System::Exception
18
+ {
19
+ public:
20
+ BurnTestException(HRESULT error)
21
+ {
22
+ this->HResult = error;
23
+ }
24
+
25
+ BurnTestException(HRESULT error, String^ message)
26
+ : Exception(message)
27
+ {
28
+ this->HResult = error;
29
+ }
30
+
31
+ property Int32 ErrorCode
32
+ {
33
+ Int32 get()
34
+ {
35
+ return this->HResult;
36
+ }
37
+ }
38
+
39
+ };
40
+}
41
+}
42
+}
43
+}
44
+}
45
+
46
+// this class is used by __TestThrowOnFailure_Format() below to deallocate
47
+// the string created after the function call has returned
48
+class __TestThrowOnFailure_StringFree
49
+{
50
+ LPWSTR m_scz;
51
+
52
+public:
53
+ __TestThrowOnFailure_StringFree(LPWSTR scz)
54
+ {
55
+ m_scz = scz;
56
+ }
57
+
58
+ ~__TestThrowOnFailure_StringFree()
59
+ {
60
+ ReleaseStr(m_scz);
61
+ }
62
+
63
+ operator LPCWSTR()
64
+ {
65
+ return m_scz;
66
+ }
67
+};
68
+
69
+// used by the TestThrowOnFailure macros to format the error string and
70
+// return an LPCWSTR that can be used to initialize a System::String
71
+#pragma warning (push)
72
+#pragma warning (disable : 4793)
73
+inline __TestThrowOnFailure_StringFree __TestThrowOnFailure_Format(LPCWSTR wzFormat, ...)
74
+{
75
+ Assert(wzFormat && *wzFormat);
76
+
77
+ HRESULT hr = S_OK;
78
+ LPWSTR scz = NULL;
79
+ va_list args;
80
+
81
+ va_start(args, wzFormat);
82
+ hr = StrAllocFormattedArgs(&scz, wzFormat, args);
83
+ va_end(args);
84
+ ExitOnFailure(hr, "Failed to format message string.");
85
+
86
+LExit:
87
+ return scz;
88
+}
89
+#pragma warning (pop)
90
+
91
+#define TestThrowOnFailure(hr, s) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(s)); }
92
+#define TestThrowOnFailure1(hr, s, p) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p))); }
93
+#define TestThrowOnFailure2(hr, s, p1, p2) if (FAILED(hr)) { throw gcnew Microsoft::Tools::WindowsInstallerXml::Test::Bootstrapper::BurnTestException(hr, gcnew System::String(__TestThrowOnFailure_Format(s, p1, p2))); }
src/test/BurnUnitTest/BurnTestFixture.h
new
+41
@@ -0,0 +1,41 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+ using namespace System;
16
+
17
+ public ref class BurnTestFixture
18
+ {
19
+ public:
20
+ BurnTestFixture()
21
+ {
22
+ HRESULT hr = XmlInitialize();
23
+ TestThrowOnFailure(hr, L"Failed to initialize XML support.");
24
+
25
+ hr = RegInitialize();
26
+ TestThrowOnFailure(hr, L"Failed to initialize Regutil.");
27
+
28
+ PlatformInitialize();
29
+ }
30
+
31
+ ~BurnTestFixture()
32
+ {
33
+ XmlUninitialize();
34
+ RegUninitialize();
35
+ }
36
+ };
37
+}
38
+}
39
+}
40
+}
41
+}
src/test/BurnUnitTest/BurnUnitTest.h
new
+55
@@ -0,0 +1,55 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+ using namespace System;
16
+ using namespace WixTest;
17
+ using namespace Xunit;
18
+
19
+ public ref class BurnUnitTest : WixTestBase, IUseFixture<BurnTestFixture^>
20
+ {
21
+ public:
22
+ BurnUnitTest()
23
+ {
24
+ }
25
+
26
+ virtual void TestInitialize() override
27
+ {
28
+ WixTestBase::TestInitialize();
29
+
30
+ HRESULT hr = S_OK;
31
+
32
+ LogInitialize(::GetModuleHandleW(NULL));
33
+
34
+ hr = LogOpen(NULL, L"BurnUnitTest", NULL, L"txt", FALSE, FALSE, NULL);
35
+ TestThrowOnFailure(hr, L"Failed to open log.");
36
+ }
37
+
38
+ virtual void TestUninitialize() override
39
+ {
40
+ LogUninitialize(FALSE);
41
+
42
+ WixTestBase::TestUninitialize();
43
+ }
44
+
45
+ virtual void SetFixture(BurnTestFixture^ fixture)
46
+ {
47
+ // Don't care about the fixture, just need it to be created and disposed.
48
+ UNREFERENCED_PARAMETER(fixture);
49
+ }
50
+ };
51
+}
52
+}
53
+}
54
+}
55
+}
src/test/BurnUnitTest/BurnUnitTest.rc
new
+7
@@ -0,0 +1,7 @@
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
+#define VER_APP
4
+#define VER_ORIGINAL_FILENAME "BurnUnitTest.dll"
5
+#define VER_INTERNAL_NAME "setup"
6
+#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests"
7
+#include "wix.rc"
src/test/BurnUnitTest/BurnUnitTest.vcxproj
new
+60
@@ -0,0 +1,60 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!-- 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. -->
3
+
4
+
5
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
6
+ <ItemGroup Label="ProjectConfigurations">
7
+ <ProjectConfiguration Include="Debug|Win32">
8
+ <Configuration>Debug</Configuration>
9
+ <Platform>Win32</Platform>
10
+ </ProjectConfiguration>
11
+ <ProjectConfiguration Include="Release|Win32">
12
+ <Configuration>Release</Configuration>
13
+ <Platform>Win32</Platform>
14
+ </ProjectConfiguration>
15
+ </ItemGroup>
16
+ <PropertyGroup Label="Globals">
17
+ <ProjectTypes>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectTypes>
18
+ <ProjectGuid>{9D1F1BA3-9393-4833-87A3-D5F1FC08EF67}</ProjectGuid>
19
+ <RootNamespace>UnitTest</RootNamespace>
20
+ <Keyword>ManagedCProj</Keyword>
21
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
22
+ <CharacterSet>Unicode</CharacterSet>
23
+ <CLRSupport>true</CLRSupport>
24
+ </PropertyGroup>
25
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.props" />
26
+ <PropertyGroup>
27
+ <ProjectAdditionalIncludeDirectories>$(WixRoot)src\libs\dutil\inc;$(WixRoot)src\burn\inc;$(WixRoot)src\burn\engine;$(WixRoot)src\libs\deputil\inc</ProjectAdditionalIncludeDirectories>
28
+ <ProjectAdditionalLinkLibraries>cabinet.lib;crypt32.lib;msi.lib;rpcrt4.lib;shlwapi.lib;wininet.lib;wintrust.lib;dutil.lib;deputil.lib;engine.lib;gdiplus.lib</ProjectAdditionalLinkLibraries>
29
+ </PropertyGroup>
30
+ <ItemGroup>
31
+ <ClCompile Include="AssemblyInfo.cpp" />
32
+ <ClCompile Include="ElevationTest.cpp" />
33
+ <ClCompile Include="ManifestHelpers.cpp" />
34
+ <ClCompile Include="ManifestTest.cpp" />
35
+ <ClCompile Include="RegistrationTest.cpp" />
36
+ <ClCompile Include="SearchTest.cpp" />
37
+ <ClCompile Include="CacheTest.cpp" />
38
+ <ClCompile Include="VariableHelpers.cpp" />
39
+ <ClCompile Include="VariableTest.cpp" />
40
+ </ItemGroup>
41
+ <ItemGroup>
42
+ <ClInclude Include="BurnTestException.h" />
43
+ <ClInclude Include="BurnTestFixture.h" />
44
+ <ClInclude Include="BurnUnitTest.h" />
45
+ <ClInclude Include="ManifestHelpers.h" />
46
+ <ClInclude Include="precomp.h" />
47
+ <ClInclude Include="VariableHelpers.h" />
48
+ </ItemGroup>
49
+ <ItemGroup>
50
+ <ResourceCompile Include="BurnUnitTest.rc" />
51
+ </ItemGroup>
52
+ <ItemGroup>
53
+ <Reference Include="System" />
54
+ <ProjectReference Include="..\..\WixTestTools\WixTestTools.csproj" />
55
+ <Reference Include="xunit, Version=1.9.2.1705, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
56
+ <HintPath>$(XunitPath)\xunit.dll</HintPath>
57
+ </Reference>
58
+ </ItemGroup>
59
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
60
+</Project>
src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
new
+74
@@ -0,0 +1,74 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <ItemGroup>
4
+ <Filter Include="Source Files">
5
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
6
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
7
+ </Filter>
8
+ <Filter Include="Header Files">
9
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
10
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
11
+ </Filter>
12
+ <Filter Include="Resource Files">
13
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
14
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
15
+ </Filter>
16
+ </ItemGroup>
17
+ <ItemGroup>
18
+ <ClCompile Include="AssemblyInfo.cpp">
19
+ <Filter>Source Files</Filter>
20
+ </ClCompile>
21
+ <ClCompile Include="ElevationTest.cpp">
22
+ <Filter>Source Files</Filter>
23
+ </ClCompile>
24
+ <ClCompile Include="ManifestHelpers.cpp">
25
+ <Filter>Source Files</Filter>
26
+ </ClCompile>
27
+ <ClCompile Include="ManifestTest.cpp">
28
+ <Filter>Source Files</Filter>
29
+ </ClCompile>
30
+ <ClCompile Include="RegistrationTest.cpp">
31
+ <Filter>Source Files</Filter>
32
+ </ClCompile>
33
+ <ClCompile Include="SearchTest.cpp">
34
+ <Filter>Source Files</Filter>
35
+ </ClCompile>
36
+ <ClCompile Include="VariableHelpers.cpp">
37
+ <Filter>Source Files</Filter>
38
+ </ClCompile>
39
+ <ClCompile Include="VariableTest.cpp">
40
+ <Filter>Source Files</Filter>
41
+ </ClCompile>
42
+ <ClCompile Include="CacheTest.cpp">
43
+ <Filter>Source Files</Filter>
44
+ </ClCompile>
45
+ <ClCompile Include="$(WixRoot)src\common\precomp.cpp">
46
+ <Filter>Source Files</Filter>
47
+ </ClCompile>
48
+ </ItemGroup>
49
+ <ItemGroup>
50
+ <ClInclude Include="BurnTestException.h">
51
+ <Filter>Header Files</Filter>
52
+ </ClInclude>
53
+ <ClInclude Include="ManifestHelpers.h">
54
+ <Filter>Header Files</Filter>
55
+ </ClInclude>
56
+ <ClInclude Include="precomp.h">
57
+ <Filter>Header Files</Filter>
58
+ </ClInclude>
59
+ <ClInclude Include="VariableHelpers.h">
60
+ <Filter>Header Files</Filter>
61
+ </ClInclude>
62
+ <ClInclude Include="BurnUnitTest.h">
63
+ <Filter>Header Files</Filter>
64
+ </ClInclude>
65
+ <ClInclude Include="BurnTestFixture.h">
66
+ <Filter>Header Files</Filter>
67
+ </ClInclude>
68
+ </ItemGroup>
69
+ <ItemGroup>
70
+ <ResourceCompile Include="BurnUnitTest.rc">
71
+ <Filter>Resource Files</Filter>
72
+ </ResourceCompile>
73
+ </ItemGroup>
74
+</Project>
\ No newline at end of file
src/test/BurnUnitTest/CacheTest.cpp
new
+72
@@ -0,0 +1,72 @@
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
+
6
+namespace Microsoft
7
+{
8
+namespace Tools
9
+{
10
+namespace WindowsInstallerXml
11
+{
12
+namespace Test
13
+{
14
+namespace Bootstrapper
15
+{
16
+ using namespace System;
17
+ using namespace System::IO;
18
+ using namespace WixTest;
19
+ using namespace Xunit;
20
+
21
+ public ref class CacheTest : BurnUnitTest
22
+ {
23
+ public:
24
+ [NamedFact]
25
+ void CacheSignatureTest()
26
+ {
27
+ HRESULT hr = S_OK;
28
+ BURN_PACKAGE package = { };
29
+ BURN_PAYLOAD payload = { };
30
+ LPWSTR sczPayloadPath = NULL;
31
+ BYTE* pb = NULL;
32
+ DWORD cb = NULL;
33
+
34
+ try
35
+ {
36
+ pin_ptr<const wchar_t> dataDirectory = PtrToStringChars(TestContext->DataDirectory);
37
+ hr = PathConcat(dataDirectory, L"BurnTestPayloads\\Products\\TestExe\\TestExe.exe", &sczPayloadPath);
38
+ Assert::True(S_OK == hr, "Failed to get path to test file.");
39
+ Assert::True(FileExistsEx(sczPayloadPath, NULL), "Test file does not exist.");
40
+
41
+ hr = StrAllocHexDecode(L"232BD16B78C1926F95D637731E1EE5379A3C4222", &pb, &cb);
42
+ Assert::Equal(S_OK, hr);
43
+
44
+ package.fPerMachine = FALSE;
45
+ package.sczCacheId = L"Bootstrapper.CacheTest.CacheSignatureTest";
46
+ payload.sczKey = L"CacheSignatureTest.PayloadKey";
47
+ payload.sczFilePath = L"CacheSignatureTest.File";
48
+ payload.pbHash = pb;
49
+ payload.cbHash = cb;
50
+
51
+ hr = CacheCompletePayload(package.fPerMachine, &payload, package.sczCacheId, sczPayloadPath, FALSE);
52
+ Assert::Equal(S_OK, hr);
53
+ }
54
+ finally
55
+ {
56
+ ReleaseMem(pb);
57
+ ReleaseStr(sczPayloadPath);
58
+
59
+ String^ filePath = Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), "Package Cache\\Bootstrapper.CacheTest.CacheSignatureTest\\CacheSignatureTest.File");
60
+ if (File::Exists(filePath))
61
+ {
62
+ File::SetAttributes(filePath, FileAttributes::Normal);
63
+ File::Delete(filePath);
64
+ }
65
+ }
66
+ }
67
+ };
68
+}
69
+}
70
+}
71
+}
72
+}
src/test/BurnUnitTest/ElevationTest.cpp
new
+218
@@ -0,0 +1,218 @@
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
+
6
+const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE;
7
+const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF;
8
+const HRESULT S_TEST_SUCCEEDED = 0x3133;
9
+const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}";
10
+
11
+
12
+static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
13
+ __inout LPSHELLEXECUTEINFOW lpExecInfo
14
+ );
15
+static DWORD CALLBACK ElevateTest_ThreadProc(
16
+ __in LPVOID lpThreadParameter
17
+ );
18
+static HRESULT ProcessParentMessages(
19
+ __in BURN_PIPE_MESSAGE* pMsg,
20
+ __in_opt LPVOID pvContext,
21
+ __out DWORD* pdwResult
22
+ );
23
+static HRESULT ProcessChildMessages(
24
+ __in BURN_PIPE_MESSAGE* pMsg,
25
+ __in_opt LPVOID pvContext,
26
+ __out DWORD* pdwResult
27
+ );
28
+
29
+namespace Microsoft
30
+{
31
+namespace Tools
32
+{
33
+namespace WindowsInstallerXml
34
+{
35
+namespace Test
36
+{
37
+namespace Bootstrapper
38
+{
39
+ using namespace System;
40
+ using namespace System::IO;
41
+ using namespace System::Threading;
42
+ using namespace WixTest;
43
+ using namespace Xunit;
44
+
45
+ public ref class ElevationTest : BurnUnitTest
46
+ {
47
+ public:
48
+ [NamedFact]
49
+ void ElevateTest()
50
+ {
51
+ HRESULT hr = S_OK;
52
+ BURN_PIPE_CONNECTION connection = { };
53
+ HANDLE hEvent = NULL;
54
+ DWORD dwResult = S_OK;
55
+ try
56
+ {
57
+ ShelFunctionOverride(ElevateTest_ShellExecuteExW);
58
+
59
+ PipeConnectionInitialize(&connection);
60
+
61
+ //
62
+ // per-user side setup
63
+ //
64
+ hr = PipeCreateNameAndSecret(&connection.sczName, &connection.sczSecret);
65
+ TestThrowOnFailure(hr, L"Failed to create connection name and secret.");
66
+
67
+ hr = PipeCreatePipes(&connection, TRUE, &hEvent);
68
+ TestThrowOnFailure(hr, L"Failed to create pipes.");
69
+
70
+ hr = PipeLaunchChildProcess(L"tests\\ignore\\this\\path\\to\\burn.exe", &connection, TRUE, NULL);
71
+ TestThrowOnFailure(hr, L"Failed to create elevated process.");
72
+
73
+ hr = PipeWaitForChildConnect(&connection);
74
+ TestThrowOnFailure(hr, L"Failed to wait for child process to connect.");
75
+
76
+ // post execute message
77
+ hr = PipeSendMessage(connection.hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult);
78
+ TestThrowOnFailure(hr, "Failed to post execute message to per-machine process.");
79
+
80
+ //
81
+ // initiate termination
82
+ //
83
+ hr = PipeTerminateChildProcess(&connection, 666, FALSE);
84
+ TestThrowOnFailure(hr, L"Failed to terminate elevated process.");
85
+
86
+ // check flags
87
+ Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult);
88
+ }
89
+ finally
90
+ {
91
+ PipeConnectionUninitialize(&connection);
92
+ ReleaseHandle(hEvent);
93
+ }
94
+ }
95
+ };
96
+}
97
+}
98
+}
99
+}
100
+}
101
+
102
+
103
+static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
104
+ __inout LPSHELLEXECUTEINFOW lpExecInfo
105
+ )
106
+{
107
+ HRESULT hr = S_OK;
108
+ LPWSTR scz = NULL;
109
+
110
+ hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0);
111
+ ExitOnFailure(hr, "Failed to copy arguments.");
112
+
113
+ // Pretend this thread is the elevated process.
114
+ lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL);
115
+ ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread.");
116
+ scz = NULL;
117
+
118
+LExit:
119
+ ReleaseStr(scz);
120
+
121
+ return SUCCEEDED(hr);
122
+}
123
+
124
+static DWORD CALLBACK ElevateTest_ThreadProc(
125
+ __in LPVOID lpThreadParameter
126
+ )
127
+{
128
+ HRESULT hr = S_OK;
129
+ LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
130
+ BURN_PIPE_CONNECTION connection = { };
131
+ BURN_PIPE_RESULT result = { };
132
+
133
+ PipeConnectionInitialize(&connection);
134
+
135
+ StrAlloc(&connection.sczName, MAX_PATH);
136
+ StrAlloc(&connection.sczSecret, MAX_PATH);
137
+
138
+ // parse command line arguments
139
+ if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId))
140
+ {
141
+ hr = E_INVALIDARG;
142
+ ExitOnFailure(hr, "Failed to parse argument string.");
143
+ }
144
+
145
+ // set up connection with per-user process
146
+ hr = PipeChildConnect(&connection, TRUE);
147
+ ExitOnFailure(hr, "Failed to connect to per-user process.");
148
+
149
+ // pump messages
150
+ hr = PipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast<LPVOID>(connection.hPipe), &result);
151
+ ExitOnFailure(hr, "Failed while pumping messages in child 'process'.");
152
+
153
+LExit:
154
+ PipeConnectionUninitialize(&connection);
155
+ ReleaseStr(sczArguments);
156
+
157
+ return FAILED(hr) ? (DWORD)hr : result.dwResult;
158
+}
159
+
160
+static HRESULT ProcessParentMessages(
161
+ __in BURN_PIPE_MESSAGE* pMsg,
162
+ __in_opt LPVOID /*pvContext*/,
163
+ __out DWORD* pdwResult
164
+ )
165
+{
166
+ HRESULT hr = S_OK;
167
+ HRESULT hrResult = E_INVALIDDATA;
168
+
169
+ // Process the message.
170
+ switch (pMsg->dwMessage)
171
+ {
172
+ case TEST_CHILD_SENT_MESSAGE_ID:
173
+ if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA)))
174
+ {
175
+ hrResult = S_TEST_SUCCEEDED;
176
+ }
177
+ break;
178
+
179
+ default:
180
+ hr = E_INVALIDARG;
181
+ ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessage);
182
+ }
183
+
184
+ *pdwResult = static_cast<DWORD>(hrResult);
185
+
186
+LExit:
187
+ return hr;
188
+}
189
+
190
+static HRESULT ProcessChildMessages(
191
+ __in BURN_PIPE_MESSAGE* pMsg,
192
+ __in_opt LPVOID pvContext,
193
+ __out DWORD* pdwResult
194
+ )
195
+{
196
+ HRESULT hr = S_OK;
197
+ HANDLE hPipe = static_cast<HANDLE>(pvContext);
198
+ DWORD dwResult = 0;
199
+
200
+ // Process the message.
201
+ switch (pMsg->dwMessage)
202
+ {
203
+ case TEST_PARENT_SENT_MESSAGE_ID:
204
+ // send test message
205
+ hr = PipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult);
206
+ ExitOnFailure(hr, "Failed to send message to per-machine process.");
207
+ break;
208
+
209
+ default:
210
+ hr = E_INVALIDARG;
211
+ ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
212
+ }
213
+
214
+ *pdwResult = dwResult;
215
+
216
+LExit:
217
+ return hr;
218
+}
src/test/BurnUnitTest/ManifestHelpers.cpp
new
+41
@@ -0,0 +1,41 @@
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
+
6
+using namespace System;
7
+using namespace Xunit;
8
+
9
+
10
+namespace Microsoft
11
+{
12
+namespace Tools
13
+{
14
+namespace WindowsInstallerXml
15
+{
16
+namespace Test
17
+{
18
+namespace Bootstrapper
19
+{
20
+ void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle)
21
+ {
22
+ HRESULT hr = S_OK;
23
+ IXMLDOMDocument* pixdDocument = NULL;
24
+ try
25
+ {
26
+ hr = XmlLoadDocument(wzDocument, &pixdDocument);
27
+ TestThrowOnFailure(hr, L"Failed to load XML document.");
28
+
29
+ hr = pixdDocument->get_documentElement(ppixeBundle);
30
+ TestThrowOnFailure(hr, L"Failed to get bundle element.");
31
+ }
32
+ finally
33
+ {
34
+ ReleaseObject(pixdDocument);
35
+ }
36
+ }
37
+}
38
+}
39
+}
40
+}
41
+}
src/test/BurnUnitTest/ManifestHelpers.h
new
+24
@@ -0,0 +1,24 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+
16
+
17
+void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle);
18
+
19
+
20
+}
21
+}
22
+}
23
+}
24
+}
src/test/BurnUnitTest/ManifestTest.cpp
new
+59
@@ -0,0 +1,59 @@
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
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+ using namespace System;
16
+ using namespace WixTest;
17
+ using namespace Xunit;
18
+
19
+ public ref class ManifestTest : BurnUnitTest
20
+ {
21
+ public:
22
+ [NamedFact]
23
+ void ManifestLoadXmlTest()
24
+ {
25
+ HRESULT hr = S_OK;
26
+ BURN_ENGINE_STATE engineState = { };
27
+ try
28
+ {
29
+ LPCSTR szDocument =
30
+ "<Bundle>"
31
+ " <UX UxDllPayloadId='ux.dll'>"
32
+ " <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
33
+ " </UX>"
34
+ " <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no' />"
35
+ " <Variable Id='Variable1' Type='numeric' Value='1' Hidden='no' Persisted='no' />"
36
+ " <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' Condition='0' />"
37
+ "</Bundle>";
38
+
39
+ hr = VariableInitialize(&engineState.variables);
40
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
41
+
42
+ // load manifest from XML
43
+ hr = ManifestLoadXmlFromBuffer((BYTE*)szDocument, lstrlenA(szDocument), &engineState);
44
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
45
+
46
+ // check variable values
47
+ Assert::True(VariableExistsHelper(&engineState.variables, L"Variable1"));
48
+ }
49
+ finally
50
+ {
51
+ //CoreUninitialize(&engineState);
52
+ }
53
+ }
54
+ };
55
+}
56
+}
57
+}
58
+}
59
+}
src/test/BurnUnitTest/RegistrationTest.cpp
new
+655
@@ -0,0 +1,655 @@
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
+
6
+#define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest"
7
+#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM"
8
+#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU"
9
+#define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
10
+#define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
11
+
12
+#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}"
13
+#define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY
14
+
15
+
16
+static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
17
+ __in HKEY hKey,
18
+ __in LPCWSTR lpSubKey,
19
+ __reserved DWORD Reserved,
20
+ __in_opt LPWSTR lpClass,
21
+ __in DWORD dwOptions,
22
+ __in REGSAM samDesired,
23
+ __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
24
+ __out PHKEY phkResult,
25
+ __out_opt LPDWORD lpdwDisposition
26
+ );
27
+static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW(
28
+ __in HKEY hKey,
29
+ __in_opt LPCWSTR lpSubKey,
30
+ __reserved DWORD ulOptions,
31
+ __in REGSAM samDesired,
32
+ __out PHKEY phkResult
33
+ );
34
+static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW(
35
+ __in HKEY hKey,
36
+ __in LPCWSTR lpSubKey,
37
+ __in REGSAM samDesired,
38
+ __reserved DWORD Reserved
39
+ );
40
+
41
+namespace Microsoft
42
+{
43
+namespace Tools
44
+{
45
+namespace WindowsInstallerXml
46
+{
47
+namespace Test
48
+{
49
+namespace Bootstrapper
50
+{
51
+ using namespace Microsoft::Win32;
52
+ using namespace System;
53
+ using namespace System::IO;
54
+ using namespace WixTest;
55
+ using namespace Xunit;
56
+
57
+ public ref class RegistrationTest : BurnUnitTest
58
+ {
59
+ public:
60
+ [NamedFact]
61
+ void RegisterBasicTest()
62
+ {
63
+ HRESULT hr = S_OK;
64
+ IXMLDOMElement* pixeBundle = NULL;
65
+ LPWSTR sczCurrentProcess = NULL;
66
+ BURN_VARIABLES variables = { };
67
+ BURN_USER_EXPERIENCE userExperience = { };
68
+ BOOTSTRAPPER_COMMAND command = { };
69
+ BURN_REGISTRATION registration = { };
70
+ BURN_LOGGING logging = { };
71
+ String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
72
+ try
73
+ {
74
+ // set mock API's
75
+ RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
76
+
77
+ Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
78
+
79
+ logging.sczPath = L"BurnUnitTest.txt";
80
+
81
+ LPCWSTR wzDocument =
82
+ L"<Bundle>"
83
+ L" <UX>"
84
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
85
+ L" </UX>"
86
+ L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
87
+ L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
88
+ L" </Registration>"
89
+ L"</Bundle>";
90
+
91
+ // load XML document
92
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
93
+
94
+ hr = VariableInitialize(&variables);
95
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
96
+
97
+ hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
98
+ TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
99
+
100
+ hr = RegistrationParseFromXml(®istration, pixeBundle);
101
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
102
+
103
+ hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
104
+ TestThrowOnFailure(hr, L"Failed to set registration resume command.");
105
+
106
+ hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
107
+ TestThrowOnFailure(hr, L"Failed to get current process path.");
108
+
109
+ // write registration
110
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
111
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
112
+
113
+ // verify that registration was created
114
+ Assert::True(Directory::Exists(cacheDirectory));
115
+ Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))));
116
+
117
+ Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
118
+ Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)));
119
+
120
+ // end session
121
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
122
+ TestThrowOnFailure(hr, L"Failed to unregister bundle.");
123
+
124
+ // verify that registration was removed
125
+ Assert::False(Directory::Exists(cacheDirectory));
126
+
127
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
128
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
129
+ }
130
+ finally
131
+ {
132
+ ReleaseStr(sczCurrentProcess);
133
+ ReleaseObject(pixeBundle);
134
+ UserExperienceUninitialize(&userExperience);
135
+ RegistrationUninitialize(®istration);
136
+ VariablesUninitialize(&variables);
137
+
138
+ Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
139
+ if (Directory::Exists(cacheDirectory))
140
+ {
141
+ Directory::Delete(cacheDirectory, true);
142
+ }
143
+
144
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
145
+ }
146
+ }
147
+
148
+ [NamedFact]
149
+ void RegisterArpMinimumTest()
150
+ {
151
+ HRESULT hr = S_OK;
152
+ IXMLDOMElement* pixeBundle = NULL;
153
+ LPWSTR sczCurrentProcess = NULL;
154
+ BURN_VARIABLES variables = { };
155
+ BURN_USER_EXPERIENCE userExperience = { };
156
+ BOOTSTRAPPER_COMMAND command = { };
157
+ BURN_REGISTRATION registration = { };
158
+ BURN_LOGGING logging = { };
159
+ String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
160
+ try
161
+ {
162
+ // set mock API's
163
+ RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
164
+
165
+ Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
166
+
167
+ logging.sczPath = L"BurnUnitTest.txt";
168
+
169
+ LPCWSTR wzDocument =
170
+ L"<Bundle>"
171
+ L" <UX>"
172
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
173
+ L" </UX>"
174
+ L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
175
+ L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' DisplayVersion='1.0.0.0' />"
176
+ L" </Registration>"
177
+ L"</Bundle>";
178
+
179
+ // load XML document
180
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
181
+
182
+ hr = VariableInitialize(&variables);
183
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
184
+
185
+ hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
186
+ TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
187
+
188
+ hr = RegistrationParseFromXml(®istration, pixeBundle);
189
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
190
+
191
+ hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
192
+ TestThrowOnFailure(hr, L"Failed to set registration resume command.");
193
+
194
+ hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
195
+ TestThrowOnFailure(hr, L"Failed to get current process path.");
196
+
197
+ //
198
+ // install
199
+ //
200
+
201
+ // write registration
202
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
203
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
204
+
205
+ // verify that registration was created
206
+ Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
207
+ Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
208
+
209
+ // complete registration
210
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
211
+ TestThrowOnFailure(hr, L"Failed to unregister bundle.");
212
+
213
+ // verify that registration was updated
214
+ Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
215
+ Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
216
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
217
+
218
+ //
219
+ // uninstall
220
+ //
221
+
222
+ // write registration
223
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0);
224
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
225
+
226
+ // verify that registration was updated
227
+ Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
228
+ Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
229
+ Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
230
+
231
+ // delete registration
232
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
233
+ TestThrowOnFailure(hr, L"Failed to unregister bundle.");
234
+
235
+ // verify that registration was removed
236
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
237
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
238
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
239
+ }
240
+ finally
241
+ {
242
+ ReleaseStr(sczCurrentProcess);
243
+ ReleaseObject(pixeBundle);
244
+ UserExperienceUninitialize(&userExperience);
245
+ RegistrationUninitialize(®istration);
246
+ VariablesUninitialize(&variables);
247
+
248
+ Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
249
+ if (Directory::Exists(cacheDirectory))
250
+ {
251
+ Directory::Delete(cacheDirectory, true);
252
+ }
253
+
254
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
255
+ }
256
+ }
257
+
258
+ [NamedFact]
259
+ void RegisterArpFullTest()
260
+ {
261
+ HRESULT hr = S_OK;
262
+ IXMLDOMElement* pixeBundle = NULL;
263
+ LPWSTR sczCurrentProcess = NULL;
264
+ BURN_VARIABLES variables = { };
265
+ BURN_USER_EXPERIENCE userExperience = { };
266
+ BOOTSTRAPPER_COMMAND command = { };
267
+ BURN_REGISTRATION registration = { };
268
+ BURN_LOGGING logging = { };
269
+ String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
270
+ try
271
+ {
272
+ // set mock API's
273
+ RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
274
+
275
+ Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
276
+
277
+ logging.sczPath = L"BurnUnitTest.txt";
278
+
279
+ LPCWSTR wzDocument =
280
+ L"<Bundle>"
281
+ L" <UX UxDllPayloadId='ux.dll'>"
282
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
283
+ L" </UX>"
284
+ L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
285
+ L" <Arp Register='yes' DisplayName='DisplayName1' DisplayVersion='1.2.3.4' Publisher='Publisher1' HelpLink='http://www.microsoft.com/help'"
286
+ L" HelpTelephone='555-555-5555' AboutUrl='http://www.microsoft.com/about' UpdateUrl='http://www.microsoft.com/update'"
287
+ L" Comments='Comments1' Contact='Contact1' DisableModify='yes' DisableRemove='yes' />"
288
+ L" </Registration>"
289
+ L"</Bundle>";
290
+
291
+ // load XML document
292
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
293
+
294
+ hr = VariableInitialize(&variables);
295
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
296
+
297
+ hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
298
+ TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
299
+
300
+ hr = RegistrationParseFromXml(®istration, pixeBundle);
301
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
302
+
303
+ hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
304
+ TestThrowOnFailure(hr, L"Failed to set registration resume command.");
305
+
306
+ hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
307
+ TestThrowOnFailure(hr, L"Failed to get current process path.");
308
+
309
+ //
310
+ // install
311
+ //
312
+
313
+ // write registration
314
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
315
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
316
+
317
+ // verify that registration was created
318
+ Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
319
+ Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
320
+
321
+ // finish registration
322
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
323
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
324
+
325
+ // verify that registration was updated
326
+ Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
327
+ Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
328
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
329
+
330
+ Assert::Equal(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
331
+ Assert::Equal(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr));
332
+ Assert::Equal(gcnew String(L"Publisher1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Publisher"), nullptr));
333
+ Assert::Equal(gcnew String(L"http://www.microsoft.com/help"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpLink"), nullptr));
334
+ Assert::Equal(gcnew String(L"555-555-5555"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"HelpTelephone"), nullptr));
335
+ Assert::Equal(gcnew String(L"http://www.microsoft.com/about"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLInfoAbout"), nullptr));
336
+ Assert::Equal(gcnew String(L"http://www.microsoft.com/update"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"URLUpdateInfo"), nullptr));
337
+ Assert::Equal(gcnew String(L"Comments1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Comments"), nullptr));
338
+ Assert::Equal(gcnew String(L"Contact1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Contact"), nullptr));
339
+ Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoModify"), nullptr));
340
+ Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"NoRemove"), nullptr));
341
+
342
+ //
343
+ // uninstall
344
+ //
345
+
346
+ // write registration
347
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, 0);
348
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
349
+
350
+ // verify that registration was updated
351
+ Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
352
+ Assert::Equal(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
353
+
354
+ // delete registration
355
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
356
+ TestThrowOnFailure(hr, L"Failed to unregister bundle.");
357
+
358
+ // verify that registration was removed
359
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
360
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
361
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
362
+ }
363
+ finally
364
+ {
365
+ ReleaseStr(sczCurrentProcess);
366
+ ReleaseObject(pixeBundle);
367
+ UserExperienceUninitialize(&userExperience);
368
+ RegistrationUninitialize(®istration);
369
+ VariablesUninitialize(&variables);
370
+
371
+ Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
372
+ if (Directory::Exists(cacheDirectory))
373
+ {
374
+ Directory::Delete(cacheDirectory, true);
375
+ }
376
+
377
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
378
+ }
379
+ }
380
+
381
+ [NamedFact]
382
+ void ResumeTest()
383
+ {
384
+ HRESULT hr = S_OK;
385
+ IXMLDOMElement* pixeBundle = NULL;
386
+ LPWSTR sczCurrentProcess = NULL;
387
+ BURN_VARIABLES variables = { };
388
+ BURN_USER_EXPERIENCE userExperience = { };
389
+ BOOTSTRAPPER_COMMAND command = { };
390
+ BURN_REGISTRATION registration = { };
391
+ BURN_LOGGING logging = { };
392
+ BYTE rgbData[256] = { };
393
+ BOOTSTRAPPER_RESUME_TYPE resumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
394
+ BYTE* pbBuffer = NULL;
395
+ DWORD cbBuffer = 0;
396
+ String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
397
+ try
398
+ {
399
+ for (DWORD i = 0; i < 256; ++i)
400
+ {
401
+ rgbData[i] = (BYTE)i;
402
+ }
403
+
404
+ // set mock API's
405
+ RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL);
406
+
407
+ Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH));
408
+
409
+ logging.sczPath = L"BurnUnitTest.txt";
410
+
411
+ LPCWSTR wzDocument =
412
+ L"<Bundle>"
413
+ L" <UX>"
414
+ L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
415
+ L" </UX>"
416
+ L" <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
417
+ L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
418
+ L" </Registration>"
419
+ L"</Bundle>";
420
+
421
+ // load XML document
422
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
423
+
424
+ hr = VariableInitialize(&variables);
425
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
426
+
427
+ hr = UserExperienceParseFromXml(&userExperience, pixeBundle);
428
+ TestThrowOnFailure(hr, L"Failed to parse UX from XML.");
429
+
430
+ hr = RegistrationParseFromXml(®istration, pixeBundle);
431
+ TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
432
+
433
+ hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging);
434
+ TestThrowOnFailure(hr, L"Failed to set registration resume command.");
435
+
436
+ hr = PathForCurrentProcess(&sczCurrentProcess, NULL);
437
+ TestThrowOnFailure(hr, L"Failed to get current process path.");
438
+
439
+ // read resume type before session
440
+ hr = RegistrationDetectResumeType(®istration, &resumeType);
441
+ TestThrowOnFailure(hr, L"Failed to read resume type.");
442
+
443
+ Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType);
444
+
445
+ // begin session
446
+ hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0);
447
+ TestThrowOnFailure(hr, L"Failed to register bundle.");
448
+
449
+ hr = RegistrationSaveState(®istration, rgbData, sizeof(rgbData));
450
+ TestThrowOnFailure(hr, L"Failed to save state.");
451
+
452
+ // read interrupted resume type
453
+ hr = RegistrationDetectResumeType(®istration, &resumeType);
454
+ TestThrowOnFailure(hr, L"Failed to read interrupted resume type.");
455
+
456
+ Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED, (int)resumeType);
457
+
458
+ // suspend session
459
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_SUSPEND, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER);
460
+ TestThrowOnFailure(hr, L"Failed to suspend session.");
461
+
462
+ // verify that run key was removed
463
+ Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
464
+
465
+ // read suspend resume type
466
+ hr = RegistrationDetectResumeType(®istration, &resumeType);
467
+ TestThrowOnFailure(hr, L"Failed to read suspend resume type.");
468
+
469
+ Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_SUSPEND, (int)resumeType);
470
+
471
+ // read state back
472
+ hr = RegistrationLoadState(®istration, &pbBuffer, &cbBuffer);
473
+ TestThrowOnFailure(hr, L"Failed to load state.");
474
+
475
+ Assert::Equal((DWORD)sizeof(rgbData), cbBuffer);
476
+ Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData)));
477
+
478
+ // write active resume mode
479
+ hr = RegistrationSessionResume(®istration, &variables);
480
+ TestThrowOnFailure(hr, L"Failed to write active resume mode.");
481
+
482
+ // verify that run key was put back
483
+ Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
484
+
485
+ // end session
486
+ hr = RegistrationSessionEnd(®istration, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER);
487
+ TestThrowOnFailure(hr, L"Failed to unregister bundle.");
488
+
489
+ // read resume type after session
490
+ hr = RegistrationDetectResumeType(®istration, &resumeType);
491
+ TestThrowOnFailure(hr, L"Failed to read resume type.");
492
+
493
+ Assert::Equal((int)BOOTSTRAPPER_RESUME_TYPE_NONE, (int)resumeType);
494
+ }
495
+ finally
496
+ {
497
+ ReleaseStr(sczCurrentProcess);
498
+ ReleaseObject(pixeBundle);
499
+ UserExperienceUninitialize(&userExperience);
500
+ RegistrationUninitialize(®istration);
501
+ VariablesUninitialize(&variables);
502
+
503
+ Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH));
504
+ if (Directory::Exists(cacheDirectory))
505
+ {
506
+ Directory::Delete(cacheDirectory, true);
507
+ }
508
+
509
+ RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
510
+ }
511
+ }
512
+
513
+ //BOOTSTRAPPER_RESUME_TYPE_NONE,
514
+ //BOOTSTRAPPER_RESUME_TYPE_INVALID, // resume information is present but invalid
515
+ //BOOTSTRAPPER_RESUME_TYPE_UNEXPECTED, // relaunched after an unexpected interruption
516
+ //BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet
517
+ //BOOTSTRAPPER_RESUME_TYPE_REBOOT, // relaunched after reboot
518
+ //BOOTSTRAPPER_RESUME_TYPE_SUSPEND, // relaunched after suspend
519
+ //BOOTSTRAPPER_RESUME_TYPE_ARP, // launched from ARP
520
+ };
521
+}
522
+}
523
+}
524
+}
525
+}
526
+
527
+
528
+static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
529
+ __in HKEY hKey,
530
+ __in LPCWSTR lpSubKey,
531
+ __reserved DWORD Reserved,
532
+ __in_opt LPWSTR lpClass,
533
+ __in DWORD dwOptions,
534
+ __in REGSAM samDesired,
535
+ __in_opt CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
536
+ __out PHKEY phkResult,
537
+ __out_opt LPDWORD lpdwDisposition
538
+ )
539
+{
540
+ LSTATUS ls = ERROR_SUCCESS;
541
+ LPCWSTR wzRoot = NULL;
542
+ HKEY hkRoot = NULL;
543
+
544
+ if (HKEY_LOCAL_MACHINE == hKey)
545
+ {
546
+ wzRoot = HKLM_PATH;
547
+ }
548
+ else if (HKEY_CURRENT_USER == hKey)
549
+ {
550
+ wzRoot = HKCU_PATH;
551
+ }
552
+ else
553
+ {
554
+ hkRoot = hKey;
555
+ }
556
+
557
+ if (wzRoot)
558
+ {
559
+ ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot);
560
+ if (ERROR_SUCCESS != ls)
561
+ {
562
+ ExitFunction();
563
+ }
564
+ }
565
+
566
+ ls = ::RegCreateKeyExW(hkRoot, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
567
+
568
+LExit:
569
+ ReleaseRegKey(hkRoot);
570
+
571
+ return ls;
572
+}
573
+
574
+static LSTATUS APIENTRY RegistrationTest_RegOpenKeyExW(
575
+ __in HKEY hKey,
576
+ __in_opt LPCWSTR lpSubKey,
577
+ __reserved DWORD ulOptions,
578
+ __in REGSAM samDesired,
579
+ __out PHKEY phkResult
580
+ )
581
+{
582
+ LSTATUS ls = ERROR_SUCCESS;
583
+ LPCWSTR wzRoot = NULL;
584
+ HKEY hkRoot = NULL;
585
+
586
+ if (HKEY_LOCAL_MACHINE == hKey)
587
+ {
588
+ wzRoot = HKLM_PATH;
589
+ }
590
+ else if (HKEY_CURRENT_USER == hKey)
591
+ {
592
+ wzRoot = HKCU_PATH;
593
+ }
594
+ else
595
+ {
596
+ hkRoot = hKey;
597
+ }
598
+
599
+ if (wzRoot)
600
+ {
601
+ ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE, &hkRoot);
602
+ if (ERROR_SUCCESS != ls)
603
+ {
604
+ ExitFunction();
605
+ }
606
+ }
607
+
608
+ ls = ::RegOpenKeyExW(hkRoot, lpSubKey, ulOptions, samDesired, phkResult);
609
+
610
+LExit:
611
+ ReleaseRegKey(hkRoot);
612
+
613
+ return ls;
614
+}
615
+
616
+static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW(
617
+ __in HKEY hKey,
618
+ __in LPCWSTR lpSubKey,
619
+ __in REGSAM samDesired,
620
+ __reserved DWORD Reserved
621
+ )
622
+{
623
+ LSTATUS ls = ERROR_SUCCESS;
624
+ LPCWSTR wzRoot = NULL;
625
+ HKEY hkRoot = NULL;
626
+
627
+ if (HKEY_LOCAL_MACHINE == hKey)
628
+ {
629
+ wzRoot = HKLM_PATH;
630
+ }
631
+ else if (HKEY_CURRENT_USER == hKey)
632
+ {
633
+ wzRoot = HKCU_PATH;
634
+ }
635
+ else
636
+ {
637
+ hkRoot = hKey;
638
+ }
639
+
640
+ if (wzRoot)
641
+ {
642
+ ls = ::RegOpenKeyExW(HKEY_CURRENT_USER, wzRoot, 0, KEY_WRITE | samDesired, &hkRoot);
643
+ if (ERROR_SUCCESS != ls)
644
+ {
645
+ ExitFunction();
646
+ }
647
+ }
648
+
649
+ ls = ::RegDeleteKeyExW(hkRoot, lpSubKey, samDesired, Reserved);
650
+
651
+LExit:
652
+ ReleaseRegKey(hkRoot);
653
+
654
+ return ls;
655
+}
src/test/BurnUnitTest/SearchTest.cpp
new
+721
@@ -0,0 +1,721 @@
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
+
6
+static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW(
7
+ __in LPCWSTR szProduct,
8
+ __in LPCWSTR szComponent,
9
+ __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
10
+ __inout_opt LPDWORD pcchBuf
11
+ );
12
+static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW(
13
+ __in LPCWSTR szComponent,
14
+ __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
15
+ __inout_opt LPDWORD pcchBuf
16
+ );
17
+static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW(
18
+ __in LPCWSTR szProductCode,
19
+ __in LPCWSTR szProperty,
20
+ __out_ecount_opt(*pcchValue) LPWSTR szValue,
21
+ __inout_opt LPDWORD pcchValue
22
+ );
23
+static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW(
24
+ __in LPCWSTR szProductCode,
25
+ __in_opt LPCWSTR szUserSid,
26
+ __in MSIINSTALLCONTEXT dwContext,
27
+ __in LPCWSTR szProperty,
28
+ __out_ecount_opt(*pcchValue) LPWSTR szValue,
29
+ __inout_opt LPDWORD pcchValue
30
+ );
31
+
32
+using namespace System;
33
+using namespace Xunit;
34
+using namespace Microsoft::Win32;
35
+
36
+namespace Microsoft
37
+{
38
+namespace Tools
39
+{
40
+namespace WindowsInstallerXml
41
+{
42
+namespace Test
43
+{
44
+namespace Bootstrapper
45
+{
46
+ public ref class SearchTest : BurnUnitTest
47
+ {
48
+ public:
49
+ [NamedFact]
50
+ void DirectorySearchTest()
51
+ {
52
+ HRESULT hr = S_OK;
53
+ IXMLDOMElement* pixeBundle = NULL;
54
+ BURN_VARIABLES variables = { };
55
+ BURN_SEARCHES searches = { };
56
+ try
57
+ {
58
+ hr = VariableInitialize(&variables);
59
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
60
+
61
+ pin_ptr<const WCHAR> wzDirectory1 = PtrToStringChars(this->TestContext->TestDirectory);
62
+ pin_ptr<const WCHAR> wzDirectory2 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none")));
63
+
64
+ VariableSetStringHelper(&variables, L"Directory1", wzDirectory1);
65
+ VariableSetStringHelper(&variables, L"Directory2", wzDirectory2);
66
+
67
+ LPCWSTR wzDocument =
68
+ L"<Bundle>"
69
+ L" <DirectorySearch Id='Search1' Type='exists' Path='[Directory1]' Variable='Variable1' />"
70
+ L" <DirectorySearch Id='Search2' Type='exists' Path='[Directory2]' Variable='Variable2' />"
71
+ L"</Bundle>";
72
+
73
+ // load XML document
74
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
75
+
76
+ hr = SearchesParseFromXml(&searches, pixeBundle);
77
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
78
+
79
+ // execute searches
80
+ hr = SearchesExecute(&searches, &variables);
81
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
82
+
83
+ // check variable values
84
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1"));
85
+ Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2"));
86
+ }
87
+ finally
88
+ {
89
+ ReleaseObject(pixeBundle);
90
+ VariablesUninitialize(&variables);
91
+ SearchesUninitialize(&searches);
92
+ }
93
+ }
94
+
95
+ [NamedFact]
96
+ void FileSearchTest()
97
+ {
98
+ HRESULT hr = S_OK;
99
+ IXMLDOMElement* pixeBundle = NULL;
100
+ BURN_VARIABLES variables = { };
101
+ BURN_SEARCHES searches = { };
102
+ ULARGE_INTEGER uliVersion = { };
103
+ try
104
+ {
105
+ hr = VariableInitialize(&variables);
106
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
107
+
108
+ pin_ptr<const WCHAR> wzFile1 = PtrToStringChars(System::IO::Path::Combine(this->TestContext->TestDirectory, gcnew String(L"none.txt")));
109
+ pin_ptr<const WCHAR> wzFile2 = PtrToStringChars(System::Reflection::Assembly::GetExecutingAssembly()->Location);
110
+
111
+ hr = FileVersion(wzFile2, &uliVersion.HighPart, &uliVersion.LowPart);
112
+ TestThrowOnFailure(hr, L"Failed to get DLL version.");
113
+
114
+ VariableSetStringHelper(&variables, L"File1", wzFile1);
115
+ VariableSetStringHelper(&variables, L"File2", wzFile2);
116
+
117
+ LPCWSTR wzDocument =
118
+ L"<Bundle>"
119
+ L" <FileSearch Id='Search1' Type='exists' Path='[File1]' Variable='Variable1' />"
120
+ L" <FileSearch Id='Search2' Type='exists' Path='[File2]' Variable='Variable2' />"
121
+ L" <FileSearch Id='Search3' Type='version' Path='[File2]' Variable='Variable3' />"
122
+ L"</Bundle>";
123
+
124
+ // load XML document
125
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
126
+
127
+ hr = SearchesParseFromXml(&searches, pixeBundle);
128
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
129
+
130
+ // execute searches
131
+ hr = SearchesExecute(&searches, &variables);
132
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
133
+
134
+ // check variable values
135
+ Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable1"));
136
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2"));
137
+ Assert::Equal(uliVersion.QuadPart, VariableGetVersionHelper(&variables, L"Variable3"));
138
+ }
139
+ finally
140
+ {
141
+ ReleaseObject(pixeBundle);
142
+ VariablesUninitialize(&variables);
143
+ SearchesUninitialize(&searches);
144
+ }
145
+ }
146
+
147
+ [NamedFact]
148
+ void RegistrySearchTest()
149
+ {
150
+ HRESULT hr = S_OK;
151
+ IXMLDOMElement* pixeBundle = NULL;
152
+ BURN_VARIABLES variables = { };
153
+ BURN_SEARCHES searches = { };
154
+ HKEY hkey32 = NULL;
155
+ HKEY hkey64 = NULL;
156
+ BOOL f64bitMachine = (nullptr != Environment::GetEnvironmentVariable("ProgramFiles(x86)"));
157
+
158
+ try
159
+ {
160
+ hr = VariableInitialize(&variables);
161
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
162
+
163
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"String"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::String);
164
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"StringExpand"), gcnew String(L"String1 %TEMP%"), RegistryValueKind::ExpandString);
165
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"DWord"), 1, RegistryValueKind::DWord);
166
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"QWord"), 1ll, RegistryValueKind::QWord);
167
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionString"), gcnew String(L"1.1.1.1"), RegistryValueKind::String);
168
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), gcnew String(L"VersionQWord"), MAKEQWORDVERSION(1,1,1,1), RegistryValueKind::QWord);
169
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String"), nullptr, gcnew String(L"String1"), RegistryValueKind::String);
170
+ Registry::SetValue(gcnew String(L"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric"), nullptr, 1ll, RegistryValueKind::DWord);
171
+
172
+ if (f64bitMachine)
173
+ {
174
+ hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_32KEY, &hkey32);
175
+ Assert::True(SUCCEEDED(hr));
176
+
177
+ hr = RegCreate(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness\\", KEY_WRITE | KEY_WOW64_64KEY, &hkey64);
178
+ Assert::True(SUCCEEDED(hr));
179
+
180
+ hr = RegWriteString(hkey64, L"TestStringSpecificToBitness", L"64-bit");
181
+ Assert::True(SUCCEEDED(hr));
182
+
183
+ hr = RegWriteString(hkey32, L"TestStringSpecificToBitness", L"32-bit");
184
+ Assert::True(SUCCEEDED(hr));
185
+ }
186
+
187
+ VariableSetStringHelper(&variables, L"MyKey", L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value");
188
+ VariableSetStringHelper(&variables, L"MyValue", L"String");
189
+
190
+ LPCWSTR wzDocument =
191
+ L"<Bundle>"
192
+ L" <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' />"
193
+ L" <RegistrySearch Id='Search2' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\None' Variable='Variable2' />"
194
+ L" <RegistrySearch Id='Search3' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='None' Variable='Variable3' />"
195
+ L" <RegistrySearch Id='Search4' Type='exists' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable4' />"
196
+ L" <RegistrySearch Id='Search5' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable5' VariableType='string' />"
197
+ L" <RegistrySearch Id='Search6' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable6' VariableType='string' ExpandEnvironment='no' />"
198
+ L" <RegistrySearch Id='Search7' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='String' Variable='Variable7' VariableType='string' ExpandEnvironment='yes' />"
199
+ L" <RegistrySearch Id='Search8' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable8' VariableType='string' />"
200
+ L" <RegistrySearch Id='Search9' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable9' VariableType='string' ExpandEnvironment='no' />"
201
+ L" <RegistrySearch Id='Search10' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='StringExpand' Variable='Variable10' VariableType='string' ExpandEnvironment='yes' />"
202
+ L" <RegistrySearch Id='Search11' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='DWord' Variable='Variable11' VariableType='numeric' />"
203
+ L" <RegistrySearch Id='Search12' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='QWord' Variable='Variable12' VariableType='numeric' />"
204
+ L" <RegistrySearch Id='Search13' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='VersionString' Variable='Variable13' VariableType='version' />"
205
+ L" <RegistrySearch Id='Search14' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value' Value='VersionQWord' Variable='Variable14' VariableType='version' />"
206
+ L" <RegistrySearch Id='Search15' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\String' Variable='Variable15' VariableType='string' />"
207
+ L" <RegistrySearch Id='Search16' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric' Variable='Variable16' VariableType='numeric' />"
208
+ L" <RegistrySearch Id='Search17' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\None' Variable='Variable17' VariableType='numeric' />"
209
+ L" <RegistrySearch Id='Search18' Type='value' Root='HKCU' Key='SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Numeric' Value='None' Variable='Variable18' VariableType='numeric' />"
210
+ L" <RegistrySearch Id='Search19' Type='exists' Root='HKCU' Key='[MyKey]' Value='[MyValue]' Variable='Variable19' />"
211
+ L" <RegistrySearch Id='Search20' Type='value' Root='HKCU' Key='[MyKey]' Value='[MyValue]' Variable='Variable20' VariableType='string' />"
212
+ L" <RegistrySearch Id='Search21' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable21' VariableType='string' Win64='no' />"
213
+ L" <RegistrySearch Id='Search22' Type='value' Root='HKCU' Key='SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness' Value='TestStringSpecificToBitness' Variable='Variable22' VariableType='string' Win64='yes' />"
214
+ L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\Environment' Variable='Variable23' />"
215
+ L" <RegistrySearch Id='Search23' Type='exists' Root='HKU' Key='.DEFAULT\\System\\NetworkServiceSidSubkeyDoesNotExist' Variable='Variable24' />"
216
+ L" <RegistrySearch Id='Search24' Type='value' Root='HKCR' Key='.msi' Variable='Variable25' VariableType='string' />"
217
+ L"</Bundle>";
218
+
219
+ // load XML document
220
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
221
+
222
+ hr = SearchesParseFromXml(&searches, pixeBundle);
223
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
224
+
225
+ // execute searches
226
+ hr = SearchesExecute(&searches, &variables);
227
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
228
+
229
+ // check variable values
230
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable1"));
231
+ Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable2"));
232
+ Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable3"));
233
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable4"));
234
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable5"));
235
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable6"));
236
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable7"));
237
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable8"));
238
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable9"));
239
+ Assert::NotEqual(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable10"));
240
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable11"));
241
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable12"));
242
+ Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable13"));
243
+ Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables, L"Variable14"));
244
+ Assert::Equal(gcnew String(L"String1"), VariableGetStringHelper(&variables, L"Variable15"));
245
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable16"));
246
+ Assert::False(VariableExistsHelper(&variables, L"Variable17"));
247
+ Assert::False(VariableExistsHelper(&variables, L"Variable18"));
248
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable19"));
249
+ Assert::Equal(gcnew String(L"String1 %TEMP%"), VariableGetStringHelper(&variables, L"Variable20"));
250
+ if (f64bitMachine)
251
+ {
252
+ Assert::Equal(gcnew String(L"32-bit"), VariableGetStringHelper(&variables, L"Variable21"));
253
+ Assert::Equal(gcnew String(L"64-bit"), VariableGetStringHelper(&variables, L"Variable22"));
254
+ }
255
+
256
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable23"));
257
+ Assert::Equal(0ll, VariableGetNumericHelper(&variables, L"Variable24"));
258
+ Assert::Equal(gcnew String(L"Msi.Package"), VariableGetStringHelper(&variables, L"Variable25"));
259
+ }
260
+ finally
261
+ {
262
+ ReleaseRegKey(hkey32);
263
+ ReleaseRegKey(hkey64);
264
+ ReleaseObject(pixeBundle);
265
+ VariablesUninitialize(&variables);
266
+ SearchesUninitialize(&searches);
267
+
268
+ Registry::CurrentUser->DeleteSubKeyTree(gcnew String(L"SOFTWARE\\Microsoft\\WiX_Burn_UnitTest"));
269
+ if (f64bitMachine)
270
+ {
271
+ RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_32BIT, FALSE);
272
+ RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_32BIT, FALSE);
273
+ RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest\\Bitness", REG_KEY_64BIT, FALSE);
274
+ RegDelete(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\CLSID\\WiX_Burn_UnitTest", REG_KEY_64BIT, FALSE);
275
+ }
276
+ }
277
+ }
278
+
279
+ [NamedFact]
280
+ void MsiComponentSearchTest()
281
+ {
282
+ HRESULT hr = S_OK;
283
+ IXMLDOMElement* pixeBundle = NULL;
284
+ BURN_VARIABLES variables = { };
285
+ BURN_SEARCHES searches = { };
286
+ try
287
+ {
288
+ hr = VariableInitialize(&variables);
289
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
290
+
291
+ // set mock API's
292
+ WiuFunctionOverride(NULL, MsiComponentSearchTest_MsiGetComponentPathW, MsiComponentSearchTest_MsiLocateComponentW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
293
+
294
+ LPCWSTR wzDocument =
295
+ L"<Bundle>"
296
+ L" <MsiComponentSearch Id='Search1' Type='state' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable1' />"
297
+ L" <MsiComponentSearch Id='Search2' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable2' />"
298
+ L" <MsiComponentSearch Id='Search3' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{BAD00000-1000-0000-0000-000000000000}' Variable='Variable3' />"
299
+ L" <MsiComponentSearch Id='Search4' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable4' />"
300
+ L" <MsiComponentSearch Id='Search5' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable5' />"
301
+ L" <MsiComponentSearch Id='Search6' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable6' />"
302
+ L" <MsiComponentSearch Id='Search7' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable7' />"
303
+ L" <MsiComponentSearch Id='Search8' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-5000-0000-000000000000}' Variable='Variable8' />"
304
+ L" <MsiComponentSearch Id='Search9' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-6000-0000-000000000000}' Variable='Variable9' />" // todo: value key path
305
+ L" <MsiComponentSearch Id='Search10' Type='state' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable10' />"
306
+ L" <MsiComponentSearch Id='Search11' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable11' />"
307
+ L" <MsiComponentSearch Id='Search12' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable12' />"
308
+ L" <MsiComponentSearch Id='Search13' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable13' />"
309
+ L" <MsiComponentSearch Id='Search14' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable14' />"
310
+ L" <MsiComponentSearch Id='Search15' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-1000-0000-000000000000}' Variable='Variable15' />"
311
+ L" <MsiComponentSearch Id='Search16' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-2000-0000-000000000000}' Variable='Variable16' />"
312
+ L" <MsiComponentSearch Id='Search17' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-3000-0000-000000000000}' Variable='Variable17' />"
313
+ L" <MsiComponentSearch Id='Search18' Type='directory' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-4000-0000-000000000000}' Variable='Variable18' />"
314
+ L" <MsiComponentSearch Id='Search19' Type='keyPath' ProductCode='{600D0000-0000-0000-0000-000000000000}' ComponentId='{600D0000-1000-7000-0000-000000000000}' Variable='Variable19' />"
315
+ L"</Bundle>";
316
+
317
+ // load XML document
318
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
319
+
320
+ hr = SearchesParseFromXml(&searches, pixeBundle);
321
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
322
+
323
+ // execute searches
324
+ hr = SearchesExecute(&searches, &variables);
325
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
326
+
327
+ // check variable values
328
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1"));
329
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable2"));
330
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable3"));
331
+ Assert::Equal(gcnew String(L"C:\\directory\\file1.txt"), VariableGetStringHelper(&variables, L"Variable4"));
332
+ Assert::Equal(gcnew String(L"C:\\directory\\file2.txt"), VariableGetStringHelper(&variables, L"Variable5"));
333
+ Assert::Equal(gcnew String(L"C:\\directory\\file3.txt"), VariableGetStringHelper(&variables, L"Variable6"));
334
+ Assert::Equal(gcnew String(L"C:\\directory\\file4.txt"), VariableGetStringHelper(&variables, L"Variable7"));
335
+ Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\"), VariableGetStringHelper(&variables, L"Variable8"));
336
+ Assert::Equal(gcnew String(L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value"), VariableGetStringHelper(&variables, L"Variable9"));
337
+ Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable10"));
338
+ Assert::Equal(3ll, VariableGetNumericHelper(&variables, L"Variable11"));
339
+ Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable12"));
340
+ Assert::Equal(4ll, VariableGetNumericHelper(&variables, L"Variable13"));
341
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable14"));
342
+ Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable15"));
343
+ Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable16"));
344
+ Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable17"));
345
+ Assert::Equal(gcnew String(L"C:\\directory\\"), VariableGetStringHelper(&variables, L"Variable18"));
346
+ Assert::Equal(gcnew String(L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt"), VariableGetStringHelper(&variables, L"Variable19"));
347
+ }
348
+ finally
349
+ {
350
+ ReleaseObject(pixeBundle);
351
+ VariablesUninitialize(&variables);
352
+ SearchesUninitialize(&searches);
353
+ }
354
+ }
355
+
356
+ [NamedFact]
357
+ void MsiProductSearchTest()
358
+ {
359
+ HRESULT hr = S_OK;
360
+ IXMLDOMElement* pixeBundle = NULL;
361
+ BURN_VARIABLES variables = { };
362
+ BURN_SEARCHES searches = { };
363
+ try
364
+ {
365
+ hr = VariableInitialize(&variables);
366
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
367
+
368
+ // set mock API's
369
+ WiuFunctionOverride(NULL, NULL, NULL, NULL, MsiProductSearchTest_MsiGetProductInfoW, MsiProductSearchTest_MsiGetProductInfoExW, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
370
+
371
+ LPCWSTR wzDocument =
372
+ L"<Bundle>"
373
+ L" <MsiProductSearch Id='Search1' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' Variable='Variable1' />"
374
+ L" <MsiProductSearch Id='Search2' Type='version' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable2' />"
375
+ L" <MsiProductSearch Id='Search3' Type='language' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable3' />"
376
+ L" <MsiProductSearch Id='Search4' Type='state' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable4' />"
377
+ L" <MsiProductSearch Id='Search5' Type='assignment' ProductCode='{600D0000-0000-0000-0000-000000000000}' Variable='Variable5' />"
378
+ L" <MsiProductSearch Id='Search6' Type='version' ProductCode='{600D0000-1000-0000-0000-000000000000}' Variable='Variable6' />"
379
+ L"</Bundle>";
380
+
381
+ // load XML document
382
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
383
+
384
+ hr = SearchesParseFromXml(&searches, pixeBundle);
385
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
386
+
387
+ // execute searches
388
+ hr = SearchesExecute(&searches, &variables);
389
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
390
+
391
+ // check variable values
392
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"Variable1"));
393
+ Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable2"));
394
+ Assert::Equal(1033ll, VariableGetNumericHelper(&variables, L"Variable3"));
395
+ Assert::Equal(5ll, VariableGetNumericHelper(&variables, L"Variable4"));
396
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable5"));
397
+ Assert::Equal(MAKEQWORDVERSION(1,0,0,0), VariableGetVersionHelper(&variables, L"Variable6"));
398
+ }
399
+ finally
400
+ {
401
+ ReleaseObject(pixeBundle);
402
+ VariablesUninitialize(&variables);
403
+ SearchesUninitialize(&searches);
404
+ }
405
+ }
406
+
407
+ [NamedFact]
408
+ void MsiFeatureSearchTest()
409
+ {
410
+ HRESULT hr = S_OK;
411
+ IXMLDOMElement* pixeBundle = NULL;
412
+ BURN_VARIABLES variables = { };
413
+ BURN_SEARCHES searches = { };
414
+ try
415
+ {
416
+ LPCWSTR wzDocument =
417
+ L"<Bundle>"
418
+ L" <MsiFeatureSearch Id='Search1' Type='state' ProductCode='{BAD00000-0000-0000-0000-000000000000}' FeatureId='' Variable='Variable1' />"
419
+ L"</Bundle>";
420
+
421
+ hr = VariableInitialize(&variables);
422
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
423
+
424
+ // load XML document
425
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
426
+
427
+ hr = SearchesParseFromXml(&searches, pixeBundle);
428
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
429
+
430
+ // execute searches
431
+ hr = SearchesExecute(&searches, &variables);
432
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
433
+ }
434
+ finally
435
+ {
436
+ ReleaseObject(pixeBundle);
437
+ VariablesUninitialize(&variables);
438
+ SearchesUninitialize(&searches);
439
+ }
440
+ }
441
+
442
+ [NamedFact]
443
+ void ConditionalSearchTest()
444
+ {
445
+ HRESULT hr = S_OK;
446
+ IXMLDOMElement* pixeBundle = NULL;
447
+ BURN_VARIABLES variables = { };
448
+ BURN_SEARCHES searches = { };
449
+ try
450
+ {
451
+ LPCWSTR wzDocument =
452
+ L"<Bundle>"
453
+ L" <RegistrySearch Id='Search1' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable1' Condition='0' />"
454
+ L" <RegistrySearch Id='Search2' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable2' Condition='1' />"
455
+ L" <RegistrySearch Id='Search3' Type='exists' Root='HKLM' Key='SOFTWARE\\Microsoft' Variable='Variable3' Condition='=' />"
456
+ L"</Bundle>";
457
+
458
+ hr = VariableInitialize(&variables);
459
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
460
+
461
+ // load XML document
462
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
463
+
464
+ hr = SearchesParseFromXml(&searches, pixeBundle);
465
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
466
+
467
+ // execute searches
468
+ hr = SearchesExecute(&searches, &variables);
469
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
470
+
471
+ // check variable values
472
+ Assert::False(VariableExistsHelper(&variables, L"Variable1"));
473
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Variable2"));
474
+ Assert::False(VariableExistsHelper(&variables, L"Variable3"));
475
+ }
476
+ finally
477
+ {
478
+ ReleaseObject(pixeBundle);
479
+ VariablesUninitialize(&variables);
480
+ SearchesUninitialize(&searches);
481
+ }
482
+ }
483
+ [NamedFact]
484
+ void NoSearchesTest()
485
+ {
486
+ HRESULT hr = S_OK;
487
+ IXMLDOMElement* pixeBundle = NULL;
488
+ BURN_VARIABLES variables = { };
489
+ BURN_SEARCHES searches = { };
490
+ try
491
+ {
492
+ LPCWSTR wzDocument =
493
+ L"<Bundle>"
494
+ L"</Bundle>";
495
+
496
+ hr = VariableInitialize(&variables);
497
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
498
+
499
+ // load XML document
500
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
501
+
502
+ hr = SearchesParseFromXml(&searches, pixeBundle);
503
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
504
+
505
+ // execute searches
506
+ hr = SearchesExecute(&searches, &variables);
507
+ TestThrowOnFailure(hr, L"Failed to execute searches.");
508
+ }
509
+ finally
510
+ {
511
+ ReleaseObject(pixeBundle);
512
+ VariablesUninitialize(&variables);
513
+ SearchesUninitialize(&searches);
514
+ }
515
+ }
516
+ };
517
+}
518
+}
519
+}
520
+}
521
+}
522
+
523
+
524
+static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiGetComponentPathW(
525
+ __in LPCWSTR szProduct,
526
+ __in LPCWSTR szComponent,
527
+ __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
528
+ __inout_opt LPDWORD pcchBuf
529
+ )
530
+{
531
+ INSTALLSTATE is = INSTALLSTATE_INVALIDARG;
532
+ String^ product = gcnew String(szProduct);
533
+
534
+ if (String::Equals(product, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}")))
535
+ {
536
+ is = INSTALLSTATE_UNKNOWN;
537
+ }
538
+ else if (String::Equals(product, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
539
+ {
540
+ is = MsiComponentSearchTest_MsiLocateComponentW(szComponent, lpPathBuf, pcchBuf);
541
+ }
542
+
543
+ return is;
544
+}
545
+
546
+static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW(
547
+ __in LPCWSTR szComponent,
548
+ __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
549
+ __inout_opt LPDWORD pcchBuf
550
+ )
551
+{
552
+ HRESULT hr = S_OK;
553
+ INSTALLSTATE is = INSTALLSTATE_INVALIDARG;
554
+ String^ component = gcnew String(szComponent);
555
+ LPCWSTR wzValue = NULL;
556
+
557
+ if (String::Equals(component, gcnew String(L"{BAD00000-1000-0000-0000-000000000000}")))
558
+ {
559
+ is = INSTALLSTATE_UNKNOWN;
560
+ }
561
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-1000-0000-000000000000}")))
562
+ {
563
+ wzValue = L"C:\\directory\\file1.txt";
564
+ is = INSTALLSTATE_LOCAL;
565
+ }
566
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-2000-0000-000000000000}")))
567
+ {
568
+ wzValue = L"C:\\directory\\file2.txt";
569
+ is = INSTALLSTATE_SOURCE;
570
+ }
571
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-3000-0000-000000000000}")))
572
+ {
573
+ wzValue = L"C:\\directory\\file3.txt";
574
+ is = INSTALLSTATE_SOURCEABSENT;
575
+ }
576
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-4000-0000-000000000000}")))
577
+ {
578
+ wzValue = L"C:\\directory\\file4.txt";
579
+ is = INSTALLSTATE_ABSENT;
580
+ }
581
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-5000-0000-000000000000}")))
582
+ {
583
+ wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\";
584
+ is = INSTALLSTATE_LOCAL;
585
+ }
586
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-6000-0000-000000000000}")))
587
+ {
588
+ wzValue = L"02:\\SOFTWARE\\Microsoft\\WiX_Burn_UnitTest\\Value";
589
+ is = INSTALLSTATE_LOCAL;
590
+ }
591
+ else if (String::Equals(component, gcnew String(L"{600D0000-1000-7000-0000-000000000000}")))
592
+ {
593
+ wzValue = L"C:\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\directory\\file5.txt";
594
+ is = INSTALLSTATE_ABSENT;
595
+ }
596
+
597
+ if (wzValue && lpPathBuf)
598
+ {
599
+ hr = ::StringCchCopyW(lpPathBuf, *pcchBuf, wzValue);
600
+ if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
601
+ {
602
+ *pcchBuf = lstrlenW(wzValue);
603
+ is = INSTALLSTATE_MOREDATA;
604
+ }
605
+ else if (FAILED(hr))
606
+ {
607
+ is = INSTALLSTATE_INVALIDARG;
608
+ }
609
+ }
610
+
611
+ return is;
612
+}
613
+
614
+static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoW(
615
+ __in LPCWSTR szProductCode,
616
+ __in LPCWSTR szProperty,
617
+ __out_ecount_opt(*pcchValue) LPWSTR szValue,
618
+ __inout_opt LPDWORD pcchValue
619
+ )
620
+{
621
+ if (String::Equals(gcnew String(szProductCode), gcnew String(L"{600D0000-0000-0000-0000-000000000000}")) &&
622
+ String::Equals(gcnew String(szProperty), gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
623
+ {
624
+ // force call to WiuGetProductInfoEx
625
+ return ERROR_UNKNOWN_PROPERTY;
626
+ }
627
+
628
+ UINT er = MsiProductSearchTest_MsiGetProductInfoExW(szProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, szProperty, szValue, pcchValue);
629
+ return er;
630
+}
631
+
632
+static UINT WINAPI MsiProductSearchTest_MsiGetProductInfoExW(
633
+ __in LPCWSTR szProductCode,
634
+ __in_opt LPCWSTR /*szUserSid*/,
635
+ __in MSIINSTALLCONTEXT dwContext,
636
+ __in LPCWSTR szProperty,
637
+ __out_ecount_opt(*pcchValue) LPWSTR szValue,
638
+ __inout_opt LPDWORD pcchValue
639
+ )
640
+{
641
+ HRESULT hr = S_OK;
642
+ DWORD er = ERROR_FUNCTION_FAILED;
643
+ LPCWSTR wzValue = NULL;
644
+
645
+ String^ productCode = gcnew String(szProductCode);
646
+ String^ _property = gcnew String(szProperty);
647
+ switch (dwContext)
648
+ {
649
+ case MSIINSTALLCONTEXT_USERMANAGED:
650
+ er = ERROR_UNKNOWN_PRODUCT;
651
+ break;
652
+ case MSIINSTALLCONTEXT_USERUNMANAGED:
653
+ if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
654
+ {
655
+ if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
656
+ {
657
+ wzValue = L"5";
658
+ }
659
+ }
660
+ break;
661
+ case MSIINSTALLCONTEXT_MACHINE:
662
+ if (String::Equals(productCode, gcnew String(L"{BAD00000-0000-0000-0000-000000000000}")))
663
+ {
664
+ er = ERROR_UNKNOWN_PRODUCT;
665
+ }
666
+ else if (String::Equals(productCode, gcnew String(L"{600D0000-0000-0000-0000-000000000000}")))
667
+ {
668
+ if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING)))
669
+ {
670
+ wzValue = L"1.0.0.0";
671
+ }
672
+ else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_LANGUAGE)))
673
+ {
674
+ wzValue = L"1033";
675
+ }
676
+ else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_ASSIGNMENTTYPE)))
677
+ {
678
+ wzValue = L"1";
679
+ }
680
+ else if (String::Equals(_property, gcnew String(INSTALLPROPERTY_PRODUCTSTATE)))
681
+ {
682
+ // try again in per-user context
683
+ er = ERROR_UNKNOWN_PRODUCT;
684
+ }
685
+ }
686
+ else if (String::Equals(productCode, gcnew String(L"{600D0000-1000-0000-0000-000000000000}")))
687
+ {
688
+ static BOOL fFlipp = FALSE;
689
+ if (fFlipp)
690
+ {
691
+ if (String::Equals(_property, gcnew String(INSTALLPROPERTY_VERSIONSTRING)))
692
+ {
693
+ wzValue = L"1.0.0.0";
694
+ }
695
+ }
696
+ else
697
+ {
698
+ *pcchValue = MAX_PATH * 2;
699
+ er = ERROR_MORE_DATA;
700
+ }
701
+ fFlipp = !fFlipp;
702
+ }
703
+ break;
704
+ }
705
+
706
+ if (wzValue)
707
+ {
708
+ hr = ::StringCchCopyW(szValue, *pcchValue, wzValue);
709
+ if (STRSAFE_E_INSUFFICIENT_BUFFER == hr)
710
+ {
711
+ *pcchValue = lstrlenW(wzValue);
712
+ er = ERROR_MORE_DATA;
713
+ }
714
+ else if (SUCCEEDED(hr))
715
+ {
716
+ er = ERROR_SUCCESS;
717
+ }
718
+ }
719
+
720
+ return er;
721
+}
src/test/BurnUnitTest/VariableHelpers.cpp
new
+199
@@ -0,0 +1,199 @@
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
+
6
+using namespace System;
7
+using namespace Xunit;
8
+
9
+
10
+namespace Microsoft
11
+{
12
+namespace Tools
13
+{
14
+namespace WindowsInstallerXml
15
+{
16
+namespace Test
17
+{
18
+namespace Bootstrapper
19
+{
20
+ void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue)
21
+ {
22
+ HRESULT hr = S_OK;
23
+
24
+ hr = VariableSetString(pVariables, wzVariable, wzValue, FALSE);
25
+ TestThrowOnFailure2(hr, L"Failed to set %s to: %s", wzVariable, wzValue);
26
+ }
27
+
28
+ void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue)
29
+ {
30
+ HRESULT hr = S_OK;
31
+
32
+ hr = VariableSetNumeric(pVariables, wzVariable, llValue, FALSE);
33
+ TestThrowOnFailure2(hr, L"Failed to set %s to: %I64d", wzVariable, llValue);
34
+ }
35
+
36
+ void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue)
37
+ {
38
+ HRESULT hr = S_OK;
39
+
40
+ hr = VariableSetVersion(pVariables, wzVariable, qwValue, FALSE);
41
+ TestThrowOnFailure2(hr, L"Failed to set %s to: 0x%016I64x", wzVariable, qwValue);
42
+ }
43
+
44
+ String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
45
+ {
46
+ HRESULT hr = S_OK;
47
+ LPWSTR scz = NULL;
48
+ try
49
+ {
50
+ hr = VariableGetString(pVariables, wzVariable, &scz);
51
+ TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
52
+
53
+ return gcnew String(scz);
54
+ }
55
+ finally
56
+ {
57
+ ReleaseStr(scz);
58
+ }
59
+ }
60
+
61
+ __int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
62
+ {
63
+ HRESULT hr = S_OK;
64
+ LONGLONG llValue = 0;
65
+
66
+ hr = VariableGetNumeric(pVariables, wzVariable, &llValue);
67
+ TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
68
+
69
+ return llValue;
70
+ }
71
+
72
+ unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
73
+ {
74
+ HRESULT hr = S_OK;
75
+ DWORD64 qwValue = 0;
76
+
77
+ hr = VariableGetVersion(pVariables, wzVariable, &qwValue);
78
+ TestThrowOnFailure1(hr, L"Failed to get: %s", wzVariable);
79
+
80
+ return qwValue;
81
+ }
82
+
83
+ String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
84
+ {
85
+ HRESULT hr = S_OK;
86
+ LPWSTR scz = NULL;
87
+ try
88
+ {
89
+ hr = VariableGetFormatted(pVariables, wzVariable, &scz);
90
+ TestThrowOnFailure1(hr, L"Failed to get formatted: %s", wzVariable);
91
+
92
+ return gcnew String(scz);
93
+ }
94
+ finally
95
+ {
96
+ ReleaseStr(scz);
97
+ }
98
+ }
99
+
100
+ String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn)
101
+ {
102
+ HRESULT hr = S_OK;
103
+ LPWSTR scz = NULL;
104
+ try
105
+ {
106
+ hr = VariableFormatString(pVariables, wzIn, &scz, NULL);
107
+ TestThrowOnFailure1(hr, L"Failed to format string: '%s'", wzIn);
108
+
109
+ return gcnew String(scz);
110
+ }
111
+ finally
112
+ {
113
+ ReleaseStr(scz);
114
+ }
115
+ }
116
+
117
+ String^ VariableEscapeStringHelper(LPCWSTR wzIn)
118
+ {
119
+ HRESULT hr = S_OK;
120
+ LPWSTR scz = NULL;
121
+ try
122
+ {
123
+ hr = VariableEscapeString(wzIn, &scz);
124
+ TestThrowOnFailure1(hr, L"Failed to escape string: '%s'", wzIn);
125
+
126
+ return gcnew String(scz);
127
+ }
128
+ finally
129
+ {
130
+ ReleaseStr(scz);
131
+ }
132
+ }
133
+
134
+ bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition)
135
+ {
136
+ HRESULT hr = S_OK;
137
+ BOOL f = FALSE;
138
+
139
+ hr = ConditionEvaluate(pVariables, wzCondition, &f);
140
+ TestThrowOnFailure1(hr, L"Failed to evaluate condition: '%s'", wzCondition);
141
+
142
+ return f ? true : false;
143
+ }
144
+
145
+ bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition)
146
+ {
147
+ HRESULT hr = S_OK;
148
+ BOOL f = FALSE;
149
+
150
+ hr = ConditionEvaluate(pVariables, wzCondition, &f);
151
+ return E_INVALIDDATA == hr ? true : false;
152
+ }
153
+
154
+ bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
155
+ {
156
+ HRESULT hr = S_OK;
157
+ BURN_VARIANT value = { };
158
+
159
+ try
160
+ {
161
+ hr = VariableGetVariant(pVariables, wzVariable, &value);
162
+ if (E_NOTFOUND == hr || value.Type == BURN_VARIANT_TYPE_NONE)
163
+ {
164
+ return false;
165
+ }
166
+ else
167
+ {
168
+ TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable);
169
+ return true;
170
+ }
171
+ }
172
+ finally
173
+ {
174
+ BVariantUninitialize(&value);
175
+ }
176
+ }
177
+
178
+ int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable)
179
+ {
180
+ HRESULT hr = S_OK;
181
+ BURN_VARIANT value = { };
182
+
183
+ try
184
+ {
185
+ hr = VariableGetVariant(pVariables, wzVariable, &value);
186
+ TestThrowOnFailure1(hr, L"Failed to find variable: '%s'", wzVariable);
187
+
188
+ return (int)value.Type;
189
+ }
190
+ finally
191
+ {
192
+ BVariantUninitialize(&value);
193
+ }
194
+ }
195
+}
196
+}
197
+}
198
+}
199
+}
src/test/BurnUnitTest/VariableHelpers.h
new
+36
@@ -0,0 +1,36 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+namespace Microsoft
6
+{
7
+namespace Tools
8
+{
9
+namespace WindowsInstallerXml
10
+{
11
+namespace Test
12
+{
13
+namespace Bootstrapper
14
+{
15
+
16
+
17
+void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue);
18
+void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue);
19
+void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue);
20
+System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
21
+__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
22
+unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
23
+System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
24
+System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn);
25
+System::String^ VariableEscapeStringHelper(LPCWSTR wzIn);
26
+bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
27
+bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
28
+bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
29
+int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
30
+
31
+
32
+}
33
+}
34
+}
35
+}
36
+}
src/test/BurnUnitTest/VariableTest.cpp
new
+480
@@ -0,0 +1,480 @@
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
+#undef GetTempPath
5
+#undef GetEnvironmentVariable
6
+
7
+namespace Microsoft
8
+{
9
+namespace Tools
10
+{
11
+namespace WindowsInstallerXml
12
+{
13
+namespace Test
14
+{
15
+namespace Bootstrapper
16
+{
17
+ using namespace System;
18
+ using namespace Xunit;
19
+
20
+ public ref class VariableTest : BurnUnitTest
21
+ {
22
+ public:
23
+ [NamedFact]
24
+ void VariablesBasicTest()
25
+ {
26
+ HRESULT hr = S_OK;
27
+ BURN_VARIABLES variables = { };
28
+ try
29
+ {
30
+ hr = VariableInitialize(&variables);
31
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
32
+
33
+ // set variables
34
+ VariableSetStringHelper(&variables, L"PROP1", L"VAL1");
35
+ VariableSetNumericHelper(&variables, L"PROP2", 2);
36
+ VariableSetStringHelper(&variables, L"PROP5", L"VAL5");
37
+ VariableSetStringHelper(&variables, L"PROP3", L"VAL3");
38
+ VariableSetStringHelper(&variables, L"PROP4", L"VAL4");
39
+ VariableSetStringHelper(&variables, L"PROP6", L"VAL6");
40
+ VariableSetStringHelper(&variables, L"PROP7", L"7");
41
+ VariableSetVersionHelper(&variables, L"PROP8", MAKEQWORDVERSION(1,1,0,0));
42
+
43
+ // set overwritten variables
44
+ VariableSetStringHelper(&variables, L"OVERWRITTEN_STRING", L"ORIGINAL");
45
+ VariableSetNumericHelper(&variables, L"OVERWRITTEN_STRING", 42);
46
+
47
+ VariableSetNumericHelper(&variables, L"OVERWRITTEN_NUMBER", 5);
48
+ VariableSetStringHelper(&variables, L"OVERWRITTEN_NUMBER", L"NEW");
49
+
50
+ // get and verify variable values
51
+ Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables, L"PROP1"));
52
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables, L"PROP2"));
53
+ Assert::Equal(gcnew String(L"2"), VariableGetStringHelper(&variables, L"PROP2"));
54
+ Assert::Equal(gcnew String(L"VAL3"), VariableGetStringHelper(&variables, L"PROP3"));
55
+ Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables, L"PROP4"));
56
+ Assert::Equal(gcnew String(L"VAL5"), VariableGetStringHelper(&variables, L"PROP5"));
57
+ Assert::Equal(gcnew String(L"VAL6"), VariableGetStringHelper(&variables, L"PROP6"));
58
+ Assert::Equal(7ll, VariableGetNumericHelper(&variables, L"PROP7"));
59
+ Assert::Equal(MAKEQWORDVERSION(1,1,0,0), VariableGetVersionHelper(&variables, L"PROP8"));
60
+ Assert::Equal(gcnew String(L"1.1.0.0"), VariableGetStringHelper(&variables, L"PROP8"));
61
+
62
+ Assert::Equal(42ll, VariableGetNumericHelper(&variables, L"OVERWRITTEN_STRING"));
63
+ Assert::Equal(gcnew String(L"NEW"), VariableGetStringHelper(&variables, L"OVERWRITTEN_NUMBER"));
64
+ }
65
+ finally
66
+ {
67
+ VariablesUninitialize(&variables);
68
+ }
69
+ }
70
+
71
+ [NamedFact]
72
+ void VariablesParseXmlTest()
73
+ {
74
+ HRESULT hr = S_OK;
75
+ IXMLDOMElement* pixeBundle = NULL;
76
+ BURN_VARIABLES variables = { };
77
+ try
78
+ {
79
+ LPCWSTR wzDocument =
80
+ L"<Bundle>"
81
+ L" <Variable Id='Var1' Type='numeric' Value='1' Hidden='no' Persisted='no' />"
82
+ L" <Variable Id='Var2' Type='string' Value='String value.' Hidden='no' Persisted='no' />"
83
+ L" <Variable Id='Var3' Type='version' Value='1.2.3.4' Hidden='no' Persisted='no' />"
84
+ L" <Variable Id='Var4' Hidden='no' Persisted='no' />"
85
+ L" <Variable Id='Var5' Type='string' Value='' Hidden='no' Persisted='no' />"
86
+ L"</Bundle>";
87
+
88
+ hr = VariableInitialize(&variables);
89
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
90
+
91
+ // load XML document
92
+ LoadBundleXmlHelper(wzDocument, &pixeBundle);
93
+
94
+ hr = VariablesParseFromXml(&variables, pixeBundle);
95
+ TestThrowOnFailure(hr, L"Failed to parse searches from XML.");
96
+
97
+ // get and verify variable values
98
+ Assert::Equal((int)BURN_VARIANT_TYPE_NUMERIC, VariableGetTypeHelper(&variables, L"Var1"));
99
+ Assert::Equal((int)BURN_VARIANT_TYPE_STRING, VariableGetTypeHelper(&variables, L"Var2"));
100
+ Assert::Equal((int)BURN_VARIANT_TYPE_VERSION, VariableGetTypeHelper(&variables, L"Var3"));
101
+ Assert::Equal((int)BURN_VARIANT_TYPE_NONE, VariableGetTypeHelper(&variables, L"Var4"));
102
+
103
+ Assert::Equal(1ll, VariableGetNumericHelper(&variables, L"Var1"));
104
+ Assert::Equal(gcnew String(L"String value."), VariableGetStringHelper(&variables, L"Var2"));
105
+ Assert::Equal(MAKEQWORDVERSION(1,2,3,4), VariableGetVersionHelper(&variables, L"Var3"));
106
+ }
107
+ finally
108
+ {
109
+ ReleaseObject(pixeBundle);
110
+ VariablesUninitialize(&variables);
111
+ }
112
+ }
113
+
114
+ [NamedFact]
115
+ void VariablesFormatTest()
116
+ {
117
+ HRESULT hr = S_OK;
118
+ BURN_VARIABLES variables = { };
119
+ LPWSTR scz = NULL;
120
+ DWORD cch = 0;
121
+ try
122
+ {
123
+ hr = VariableInitialize(&variables);
124
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
125
+
126
+ // set variables
127
+ VariableSetStringHelper(&variables, L"PROP1", L"VAL1");
128
+ VariableSetStringHelper(&variables, L"PROP2", L"VAL2");
129
+ VariableSetNumericHelper(&variables, L"PROP3", 3);
130
+
131
+ // test string formatting
132
+ Assert::Equal(gcnew String(L"NOPROP"), VariableFormatStringHelper(&variables, L"NOPROP"));
133
+ Assert::Equal(gcnew String(L"VAL1"), VariableFormatStringHelper(&variables, L"[PROP1]"));
134
+ Assert::Equal(gcnew String(L" VAL1 "), VariableFormatStringHelper(&variables, L" [PROP1] "));
135
+ Assert::Equal(gcnew String(L"PRE VAL1"), VariableFormatStringHelper(&variables, L"PRE [PROP1]"));
136
+ Assert::Equal(gcnew String(L"VAL1 POST"), VariableFormatStringHelper(&variables, L"[PROP1] POST"));
137
+ Assert::Equal(gcnew String(L"PRE VAL1 POST"), VariableFormatStringHelper(&variables, L"PRE [PROP1] POST"));
138
+ Assert::Equal(gcnew String(L"VAL1 MID VAL2"), VariableFormatStringHelper(&variables, L"[PROP1] MID [PROP2]"));
139
+ Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[NONE]"));
140
+ Assert::Equal(gcnew String(L""), VariableFormatStringHelper(&variables, L"[prop1]"));
141
+ Assert::Equal(gcnew String(L"["), VariableFormatStringHelper(&variables, L"[\\[]"));
142
+ Assert::Equal(gcnew String(L"]"), VariableFormatStringHelper(&variables, L"[\\]]"));
143
+ Assert::Equal(gcnew String(L"[]"), VariableFormatStringHelper(&variables, L"[]"));
144
+ Assert::Equal(gcnew String(L"[NONE"), VariableFormatStringHelper(&variables, L"[NONE"));
145
+ Assert::Equal(gcnew String(L"VAL2"), VariableGetFormattedHelper(&variables, L"PROP2"));
146
+ Assert::Equal(gcnew String(L"3"), VariableGetFormattedHelper(&variables, L"PROP3"));
147
+
148
+ hr = VariableFormatString(&variables, L"PRE [PROP1] POST", &scz, &cch);
149
+ TestThrowOnFailure(hr, L"Failed to format string");
150
+
151
+ Assert::Equal((DWORD)lstrlenW(scz), cch);
152
+
153
+ hr = VariableFormatString(&variables, L"PRE [PROP1] POST", NULL, &cch);
154
+ TestThrowOnFailure(hr, L"Failed to format string");
155
+
156
+ Assert::Equal((DWORD)lstrlenW(scz), cch);
157
+ }
158
+ finally
159
+ {
160
+ VariablesUninitialize(&variables);
161
+ ReleaseStr(scz);
162
+ }
163
+ }
164
+
165
+ [NamedFact]
166
+ void VariablesEscapeTest()
167
+ {
168
+ // test string escaping
169
+ Assert::Equal(gcnew String(L"[\\[]"), VariableEscapeStringHelper(L"["));
170
+ Assert::Equal(gcnew String(L"[\\]]"), VariableEscapeStringHelper(L"]"));
171
+ Assert::Equal(gcnew String(L" [\\[]TEXT[\\]] "), VariableEscapeStringHelper(L" [TEXT] "));
172
+ }
173
+
174
+ [NamedFact]
175
+ void VariablesConditionTest()
176
+ {
177
+ HRESULT hr = S_OK;
178
+ BURN_VARIABLES variables = { };
179
+ try
180
+ {
181
+ hr = VariableInitialize(&variables);
182
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
183
+
184
+ // set variables
185
+ VariableSetStringHelper(&variables, L"PROP1", L"VAL1");
186
+ VariableSetStringHelper(&variables, L"PROP2", L"VAL2");
187
+ VariableSetStringHelper(&variables, L"PROP3", L"VAL3");
188
+ VariableSetStringHelper(&variables, L"PROP4", L"BEGIN MID END");
189
+ VariableSetNumericHelper(&variables, L"PROP5", 5);
190
+ VariableSetNumericHelper(&variables, L"PROP6", 6);
191
+ VariableSetStringHelper(&variables, L"PROP7", L"");
192
+ VariableSetNumericHelper(&variables, L"PROP8", 0);
193
+ VariableSetStringHelper(&variables, L"_PROP9", L"VAL9");
194
+ VariableSetNumericHelper(&variables, L"PROP10", -10);
195
+ VariableSetNumericHelper(&variables, L"PROP11", 9223372036854775807ll);
196
+ VariableSetNumericHelper(&variables, L"PROP12", -9223372036854775808ll);
197
+ VariableSetNumericHelper(&variables, L"PROP13", 0x00010000);
198
+ VariableSetNumericHelper(&variables, L"PROP14", 0x00000001);
199
+ VariableSetNumericHelper(&variables, L"PROP15", 0x00010001);
200
+ VariableSetVersionHelper(&variables, L"PROP16", MAKEQWORDVERSION(0,0,0,0));
201
+ VariableSetVersionHelper(&variables, L"PROP17", MAKEQWORDVERSION(1,0,0,0));
202
+ VariableSetVersionHelper(&variables, L"PROP18", MAKEQWORDVERSION(1,1,0,0));
203
+ VariableSetVersionHelper(&variables, L"PROP19", MAKEQWORDVERSION(1,1,1,0));
204
+ VariableSetVersionHelper(&variables, L"PROP20", MAKEQWORDVERSION(1,1,1,1));
205
+ VariableSetNumericHelper(&variables, L"vPROP21", 1);
206
+ VariableSetVersionHelper(&variables, L"PROP22", MAKEQWORDVERSION(65535,65535,65535,65535));
207
+ VariableSetStringHelper(&variables, L"PROP23", L"1.1.1");
208
+
209
+ // test conditions
210
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1"));
211
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5"));
212
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP7"));
213
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP8"));
214
+ Assert::True(EvaluateConditionHelper(&variables, L"_PROP9"));
215
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP16"));
216
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP17"));
217
+
218
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\""));
219
+ Assert::False(EvaluateConditionHelper(&variables, L"NONE = \"NOT\""));
220
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 <> \"VAL1\""));
221
+ Assert::True(EvaluateConditionHelper(&variables, L"NONE <> \"NOT\""));
222
+
223
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 ~= \"val1\""));
224
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"val1\""));
225
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 ~<> \"val1\""));
226
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> \"val1\""));
227
+
228
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 = 5"));
229
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 = 0"));
230
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <> 5"));
231
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <> 0"));
232
+
233
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP10 = -10"));
234
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP10 <> -10"));
235
+
236
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1"));
237
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP17 = v0"));
238
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP17 <> v1"));
239
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP17 <> v0"));
240
+
241
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP16 = v0"));
242
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP17 = v1"));
243
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP18 = v1.1"));
244
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP19 = v1.1.1"));
245
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP20 = v1.1.1.1"));
246
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.0"));
247
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP20 = v1.1.1.1.1"));
248
+ Assert::True(EvaluateConditionHelper(&variables, L"vPROP21 = 1"));
249
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP23 = v1.1.1"));
250
+ Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 = PROP23"));
251
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 <> v1.1.1"));
252
+ Assert::True(EvaluateConditionHelper(&variables, L"v1.1.1 <> PROP1"));
253
+
254
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775806"));
255
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP11 = 9223372036854775807"));
256
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 9223372036854775808"));
257
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP11 = 92233720368547758070000"));
258
+
259
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775807"));
260
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP12 = -9223372036854775808"));
261
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -9223372036854775809"));
262
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP12 = -92233720368547758080000"));
263
+
264
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP22 = v65535.65535.65535.65535"));
265
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65536.65535.65535.65535"));
266
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"PROP22 = v65535.655350000.65535.65535"));
267
+
268
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 < 6"));
269
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 < 5"));
270
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 > 4"));
271
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 > 5"));
272
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 6"));
273
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 <= 5"));
274
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 <= 4"));
275
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 4"));
276
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP5 >= 5"));
277
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP5 >= 6"));
278
+
279
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP4 << \"BEGIN\""));
280
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP4 << \"END\""));
281
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >> \"END\""));
282
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >> \"BEGIN\""));
283
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP4 >< \"MID\""));
284
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP4 >< \"NONE\""));
285
+
286
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP16 < v1.1"));
287
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP16 < v0"));
288
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP17 > v0.12"));
289
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP17 > v1"));
290
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.0"));
291
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP18 >= v1.1"));
292
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP18 >= v2.1"));
293
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1234.1"));
294
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP19 <= v1.1.1"));
295
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP19 <= v1.0.123"));
296
+
297
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP6 = \"6\""));
298
+ Assert::True(EvaluateConditionHelper(&variables, L"\"6\" = PROP6"));
299
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP6 = \"ABC\""));
300
+ Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6"));
301
+ Assert::False(EvaluateConditionHelper(&variables, L"\"ABC\" = PROP6"));
302
+
303
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP13 << 1"));
304
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP13 << 0"));
305
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP14 >> 1"));
306
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP14 >> 0"));
307
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP15 >< 65537"));
308
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP15 >< 0"));
309
+
310
+ Assert::False(EvaluateConditionHelper(&variables, L"NOT PROP1"));
311
+ Assert::True(EvaluateConditionHelper(&variables, L"NOT (PROP1 <> \"VAL1\")"));
312
+
313
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
314
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
315
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\""));
316
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\""));
317
+
318
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\""));
319
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\""));
320
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\""));
321
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\""));
322
+
323
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\""));
324
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\""));
325
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\""));
326
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")"));
327
+ Assert::True(EvaluateConditionHelper(&variables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\""));
328
+
329
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
330
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
331
+ Assert::False(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
332
+ Assert::True(EvaluateConditionHelper(&variables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\""));
333
+ Assert::True(EvaluateConditionHelper(&variables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")"));
334
+
335
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"="));
336
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1"));
337
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"(PROP1 = \""));
338
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"1A"));
339
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"*"));
340
+
341
+ Assert::True(EvaluateFailureConditionHelper(&variables, L"1 == 1"));
342
+ }
343
+ finally
344
+ {
345
+ VariablesUninitialize(&variables);
346
+ }
347
+ }
348
+
349
+ [NamedFact]
350
+ void VariablesSerializationTest()
351
+ {
352
+ HRESULT hr = S_OK;
353
+ BYTE* pbBuffer = NULL;
354
+ SIZE_T cbBuffer = 0;
355
+ SIZE_T iBuffer = 0;
356
+ BURN_VARIABLES variables1 = { };
357
+ BURN_VARIABLES variables2 = { };
358
+ try
359
+ {
360
+ // serialize
361
+ hr = VariableInitialize(&variables1);
362
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
363
+
364
+ VariableSetStringHelper(&variables1, L"PROP1", L"VAL1");
365
+ VariableSetNumericHelper(&variables1, L"PROP2", 2);
366
+ VariableSetVersionHelper(&variables1, L"PROP3", MAKEQWORDVERSION(1,1,1,1));
367
+ VariableSetStringHelper(&variables1, L"PROP4", L"VAL4");
368
+
369
+ hr = VariableSerialize(&variables1, FALSE, &pbBuffer, &cbBuffer);
370
+ TestThrowOnFailure(hr, L"Failed to serialize variables.");
371
+
372
+ // deserialize
373
+ hr = VariableInitialize(&variables2);
374
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
375
+
376
+ hr = VariableDeserialize(&variables2, FALSE, pbBuffer, cbBuffer, &iBuffer);
377
+ TestThrowOnFailure(hr, L"Failed to deserialize variables.");
378
+
379
+ Assert::Equal(gcnew String(L"VAL1"), VariableGetStringHelper(&variables2, L"PROP1"));
380
+ Assert::Equal(2ll, VariableGetNumericHelper(&variables2, L"PROP2"));
381
+ Assert::Equal(MAKEQWORDVERSION(1,1,1,1), VariableGetVersionHelper(&variables2, L"PROP3"));
382
+ Assert::Equal(gcnew String(L"VAL4"), VariableGetStringHelper(&variables2, L"PROP4"));
383
+ }
384
+ finally
385
+ {
386
+ ReleaseBuffer(pbBuffer);
387
+ VariablesUninitialize(&variables1);
388
+ VariablesUninitialize(&variables2);
389
+ }
390
+ }
391
+
392
+ [NamedFact]
393
+ void VariablesBuiltInTest()
394
+ {
395
+ HRESULT hr = S_OK;
396
+ BURN_VARIABLES variables = { };
397
+ try
398
+ {
399
+ hr = VariableInitialize(&variables);
400
+ TestThrowOnFailure(hr, L"Failed to initialize variables.");
401
+
402
+ // VersionMsi
403
+ Assert::True(EvaluateConditionHelper(&variables, L"VersionMsi >= v1.1"));
404
+
405
+ // VersionNT
406
+ Assert::True(EvaluateConditionHelper(&variables, L"VersionNT <> v0.0.0.0"));
407
+
408
+ // VersionNT64
409
+ if (nullptr == Environment::GetEnvironmentVariable("ProgramFiles(x86)"))
410
+ {
411
+ Assert::False(EvaluateConditionHelper(&variables, L"VersionNT64"));
412
+ }
413
+ else
414
+ {
415
+ Assert::True(EvaluateConditionHelper(&variables, L"VersionNT64"));
416
+ }
417
+
418
+ // attempt to set a built-in property
419
+ hr = VariableSetString(&variables, L"VersionNT", L"VAL", FALSE);
420
+ Assert::Equal(E_INVALIDARG, hr);
421
+ Assert::False(EvaluateConditionHelper(&variables, L"VersionNT = \"VAL\""));
422
+
423
+ VariableGetNumericHelper(&variables, L"NTProductType");
424
+ VariableGetNumericHelper(&variables, L"NTSuiteBackOffice");
425
+ VariableGetNumericHelper(&variables, L"NTSuiteDataCenter");
426
+ VariableGetNumericHelper(&variables, L"NTSuiteEnterprise");
427
+ VariableGetNumericHelper(&variables, L"NTSuitePersonal");
428
+ VariableGetNumericHelper(&variables, L"NTSuiteSmallBusiness");
429
+ VariableGetNumericHelper(&variables, L"NTSuiteSmallBusinessRestricted");
430
+ VariableGetNumericHelper(&variables, L"NTSuiteWebServer");
431
+ VariableGetNumericHelper(&variables, L"CompatibilityMode");
432
+ VariableGetNumericHelper(&variables, L"Privileged");
433
+ VariableGetNumericHelper(&variables, L"SystemLanguageID");
434
+ VariableGetNumericHelper(&variables, L"TerminalServer");
435
+ VariableGetNumericHelper(&variables, L"UserUILanguageID");
436
+ VariableGetNumericHelper(&variables, L"UserLanguageID");
437
+
438
+ // known folders
439
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder"));
440
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonApplicationData) + "\\", VariableGetStringHelper(&variables, L"CommonAppDataFolder"));
441
+
442
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::ProgramFiles) + "\\", VariableGetStringHelper(&variables, L"ProgramFilesFolder"));
443
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory) + "\\", VariableGetStringHelper(&variables, L"DesktopFolder"));
444
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Favorites) + "\\", VariableGetStringHelper(&variables, L"FavoritesFolder"));
445
+ VariableGetStringHelper(&variables, L"FontsFolder");
446
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData) + "\\", VariableGetStringHelper(&variables, L"LocalAppDataFolder"));
447
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Personal) + "\\", VariableGetStringHelper(&variables, L"PersonalFolder"));
448
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Programs) + "\\", VariableGetStringHelper(&variables, L"ProgramMenuFolder"));
449
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::SendTo) + "\\", VariableGetStringHelper(&variables, L"SendToFolder"));
450
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::StartMenu) + "\\", VariableGetStringHelper(&variables, L"StartMenuFolder"));
451
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Startup) + "\\", VariableGetStringHelper(&variables, L"StartupFolder"));
452
+ VariableGetStringHelper(&variables, L"SystemFolder");
453
+ VariableGetStringHelper(&variables, L"WindowsFolder");
454
+ VariableGetStringHelper(&variables, L"WindowsVolume");
455
+
456
+ Assert::Equal(System::IO::Path::GetTempPath(), System::IO::Path::GetFullPath(VariableGetStringHelper(&variables, L"TempFolder")));
457
+
458
+ VariableGetStringHelper(&variables, L"AdminToolsFolder");
459
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::CommonProgramFiles) + "\\", VariableGetStringHelper(&variables, L"CommonFilesFolder"));
460
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::MyPictures) + "\\", VariableGetStringHelper(&variables, L"MyPicturesFolder"));
461
+ Assert::Equal(Environment::GetFolderPath(Environment::SpecialFolder::Templates) + "\\", VariableGetStringHelper(&variables, L"TemplateFolder"));
462
+
463
+ if (Environment::Is64BitOperatingSystem)
464
+ {
465
+ VariableGetStringHelper(&variables, L"ProgramFiles64Folder");
466
+ VariableGetStringHelper(&variables, L"CommonFiles64Folder");
467
+ VariableGetStringHelper(&variables, L"System64Folder");
468
+ }
469
+ }
470
+ finally
471
+ {
472
+ VariablesUninitialize(&variables);
473
+ }
474
+ }
475
+ };
476
+}
477
+}
478
+}
479
+}
480
+}
src/test/BurnUnitTest/precomp.cpp
new
+3
@@ -0,0 +1,3 @@
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"
src/test/BurnUnitTest/precomp.h
new
+78
@@ -0,0 +1,78 @@
1
+#pragma once
2
+// 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.
3
+
4
+
5
+#include <windows.h>
6
+#include <Bits.h>
7
+#include <msiquery.h>
8
+#include <objbase.h>
9
+#include <shlobj.h>
10
+#include <shlwapi.h>
11
+#include <stdlib.h>
12
+#include <strsafe.h>
13
+#include "wininet.h"
14
+
15
+#include <dutil.h>
16
+#include <cryputil.h>
17
+#include <dlutil.h>
18
+#include <buffutil.h>
19
+#include <dirutil.h>
20
+#include <fileutil.h>
21
+#include <logutil.h>
22
+#include <memutil.h>
23
+#include <pathutil.h>
24
+#include <regutil.h>
25
+#include <resrutil.h>
26
+#include <shelutil.h>
27
+#include <strutil.h>
28
+#include <wiutil.h>
29
+#include <xmlutil.h>
30
+#include <dictutil.h>
31
+#include <deputil.h>
32
+
33
+#include <wixver.h>
34
+
35
+#include "BootstrapperEngine.h"
36
+#include "BootstrapperApplication.h"
37
+
38
+#include "platform.h"
39
+#include "variant.h"
40
+#include "variable.h"
41
+#include "condition.h"
42
+#include "search.h"
43
+#include "section.h"
44
+#include "approvedexe.h"
45
+#include "container.h"
46
+#include "catalog.h"
47
+#include "payload.h"
48
+#include "cabextract.h"
49
+#include "userexperience.h"
50
+#include "package.h"
51
+#include "update.h"
52
+#include "pseudobundle.h"
53
+#include "registration.h"
54
+#include "plan.h"
55
+#include "pipe.h"
56
+#include "logging.h"
57
+#include "core.h"
58
+#include "cache.h"
59
+#include "apply.h"
60
+#include "exeengine.h"
61
+#include "msiengine.h"
62
+#include "mspengine.h"
63
+#include "msuengine.h"
64
+#include "dependency.h"
65
+#include "elevation.h"
66
+#include "embedded.h"
67
+#include "manifest.h"
68
+#include "splashscreen.h"
69
+#include "bitsengine.h"
70
+
71
+#pragma managed
72
+#include <vcclr.h>
73
+
74
+#include "BurnTestException.h"
75
+#include "BurnTestFixture.h"
76
+#include "BurnUnitTest.h"
77
+#include "VariableHelpers.h"
78
+#include "ManifestHelpers.h"