aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/test/BurnUnitTest/AssemblyInfo.cpp12
-rw-r--r--src/test/BurnUnitTest/BurnTestException.h93
-rw-r--r--src/test/BurnUnitTest/BurnTestFixture.h41
-rw-r--r--src/test/BurnUnitTest/BurnUnitTest.h55
-rw-r--r--src/test/BurnUnitTest/BurnUnitTest.rc7
-rw-r--r--src/test/BurnUnitTest/BurnUnitTest.vcxproj60
-rw-r--r--src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters74
-rw-r--r--src/test/BurnUnitTest/CacheTest.cpp72
-rw-r--r--src/test/BurnUnitTest/ElevationTest.cpp218
-rw-r--r--src/test/BurnUnitTest/ManifestHelpers.cpp41
-rw-r--r--src/test/BurnUnitTest/ManifestHelpers.h24
-rw-r--r--src/test/BurnUnitTest/ManifestTest.cpp59
-rw-r--r--src/test/BurnUnitTest/RegistrationTest.cpp655
-rw-r--r--src/test/BurnUnitTest/SearchTest.cpp721
-rw-r--r--src/test/BurnUnitTest/VariableHelpers.cpp199
-rw-r--r--src/test/BurnUnitTest/VariableHelpers.h36
-rw-r--r--src/test/BurnUnitTest/VariableTest.cpp480
-rw-r--r--src/test/BurnUnitTest/precomp.cpp3
-rw-r--r--src/test/BurnUnitTest/precomp.h78
19 files changed, 2928 insertions, 0 deletions
diff --git a/src/test/BurnUnitTest/AssemblyInfo.cpp b/src/test/BurnUnitTest/AssemblyInfo.cpp
new file mode 100644
index 00000000..0282b1b7
--- /dev/null
+++ b/src/test/BurnUnitTest/AssemblyInfo.cpp
@@ -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
5using namespace System::Reflection;
6using namespace System::Runtime::CompilerServices;
7using 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)];
diff --git a/src/test/BurnUnitTest/BurnTestException.h b/src/test/BurnUnitTest/BurnTestException.h
new file mode 100644
index 00000000..bd94b4fc
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnTestException.h
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace 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
48class __TestThrowOnFailure_StringFree
49{
50 LPWSTR m_scz;
51
52public:
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)
73inline __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
86LExit:
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))); }
diff --git a/src/test/BurnUnitTest/BurnTestFixture.h b/src/test/BurnUnitTest/BurnTestFixture.h
new file mode 100644
index 00000000..b89fe6fa
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnTestFixture.h
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace 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}
diff --git a/src/test/BurnUnitTest/BurnUnitTest.h b/src/test/BurnUnitTest/BurnUnitTest.h
new file mode 100644
index 00000000..a4ca2707
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnUnitTest.h
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace 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}
diff --git a/src/test/BurnUnitTest/BurnUnitTest.rc b/src/test/BurnUnitTest/BurnUnitTest.rc
new file mode 100644
index 00000000..159b0b42
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnUnitTest.rc
@@ -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"
diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/test/BurnUnitTest/BurnUnitTest.vcxproj
new file mode 100644
index 00000000..5157d0d6
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj
@@ -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>
diff --git a/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
new file mode 100644
index 00000000..bee5d15e
--- /dev/null
+++ b/src/test/BurnUnitTest/BurnUnitTest.vcxproj.filters
@@ -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
diff --git a/src/test/BurnUnitTest/CacheTest.cpp b/src/test/BurnUnitTest/CacheTest.cpp
new file mode 100644
index 00000000..9b3c6e64
--- /dev/null
+++ b/src/test/BurnUnitTest/CacheTest.cpp
@@ -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
6namespace Microsoft
7{
8namespace Tools
9{
10namespace WindowsInstallerXml
11{
12namespace Test
13{
14namespace Bootstrapper
15{
16 using namespace System;
17 using namespace System::IO;
18 using namespace 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}
diff --git a/src/test/BurnUnitTest/ElevationTest.cpp b/src/test/BurnUnitTest/ElevationTest.cpp
new file mode 100644
index 00000000..bb10ce43
--- /dev/null
+++ b/src/test/BurnUnitTest/ElevationTest.cpp
@@ -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
6const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE;
7const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF;
8const HRESULT S_TEST_SUCCEEDED = 0x3133;
9const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}";
10
11
12static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
13 __inout LPSHELLEXECUTEINFOW lpExecInfo
14 );
15static DWORD CALLBACK ElevateTest_ThreadProc(
16 __in LPVOID lpThreadParameter
17 );
18static HRESULT ProcessParentMessages(
19 __in BURN_PIPE_MESSAGE* pMsg,
20 __in_opt LPVOID pvContext,
21 __out DWORD* pdwResult
22 );
23static HRESULT ProcessChildMessages(
24 __in BURN_PIPE_MESSAGE* pMsg,
25 __in_opt LPVOID pvContext,
26 __out DWORD* pdwResult
27 );
28
29namespace Microsoft
30{
31namespace Tools
32{
33namespace WindowsInstallerXml
34{
35namespace Test
36{
37namespace 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
103static 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
118LExit:
119 ReleaseStr(scz);
120
121 return SUCCEEDED(hr);
122}
123
124static 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
153LExit:
154 PipeConnectionUninitialize(&connection);
155 ReleaseStr(sczArguments);
156
157 return FAILED(hr) ? (DWORD)hr : result.dwResult;
158}
159
160static 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
186LExit:
187 return hr;
188}
189
190static 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
216LExit:
217 return hr;
218}
diff --git a/src/test/BurnUnitTest/ManifestHelpers.cpp b/src/test/BurnUnitTest/ManifestHelpers.cpp
new file mode 100644
index 00000000..96d5fab4
--- /dev/null
+++ b/src/test/BurnUnitTest/ManifestHelpers.cpp
@@ -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
6using namespace System;
7using namespace Xunit;
8
9
10namespace Microsoft
11{
12namespace Tools
13{
14namespace WindowsInstallerXml
15{
16namespace Test
17{
18namespace 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}
diff --git a/src/test/BurnUnitTest/ManifestHelpers.h b/src/test/BurnUnitTest/ManifestHelpers.h
new file mode 100644
index 00000000..e3e57555
--- /dev/null
+++ b/src/test/BurnUnitTest/ManifestHelpers.h
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15
16
17void LoadBundleXmlHelper(LPCWSTR wzDocument, IXMLDOMElement** ppixeBundle);
18
19
20}
21}
22}
23}
24}
diff --git a/src/test/BurnUnitTest/ManifestTest.cpp b/src/test/BurnUnitTest/ManifestTest.cpp
new file mode 100644
index 00000000..14ead82e
--- /dev/null
+++ b/src/test/BurnUnitTest/ManifestTest.cpp
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace 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}
diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp
new file mode 100644
index 00000000..1ab6b8e9
--- /dev/null
+++ b/src/test/BurnUnitTest/RegistrationTest.cpp
@@ -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
16static 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 );
27static 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 );
34static LSTATUS APIENTRY RegistrationTest_RegDeleteKeyExW(
35 __in HKEY hKey,
36 __in LPCWSTR lpSubKey,
37 __in REGSAM samDesired,
38 __reserved DWORD Reserved
39 );
40
41namespace Microsoft
42{
43namespace Tools
44{
45namespace WindowsInstallerXml
46{
47namespace Test
48{
49namespace 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(&registration, pixeBundle);
101 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
102
103 hr = PlanSetResumeCommand(&registration, 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, &registration, &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(&registration, 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(&registration);
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(&registration, pixeBundle);
189 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
190
191 hr = PlanSetResumeCommand(&registration, 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, &registration, &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(&registration, 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, &registration, &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(&registration, 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(&registration);
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(&registration, pixeBundle);
301 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
302
303 hr = PlanSetResumeCommand(&registration, 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, &registration, &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(&registration, 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, &registration, &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(&registration, 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(&registration);
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(&registration, pixeBundle);
431 TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
432
433 hr = PlanSetResumeCommand(&registration, 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(&registration, &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, &registration, &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(&registration, rgbData, sizeof(rgbData));
450 TestThrowOnFailure(hr, L"Failed to save state.");
451
452 // read interrupted resume type
453 hr = RegistrationDetectResumeType(&registration, &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(&registration, 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(&registration, &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(&registration, &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(&registration, &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(&registration, 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(&registration, &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(&registration);
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
528static 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
568LExit:
569 ReleaseRegKey(hkRoot);
570
571 return ls;
572}
573
574static 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
610LExit:
611 ReleaseRegKey(hkRoot);
612
613 return ls;
614}
615
616static 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
651LExit:
652 ReleaseRegKey(hkRoot);
653
654 return ls;
655}
diff --git a/src/test/BurnUnitTest/SearchTest.cpp b/src/test/BurnUnitTest/SearchTest.cpp
new file mode 100644
index 00000000..d03db84c
--- /dev/null
+++ b/src/test/BurnUnitTest/SearchTest.cpp
@@ -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
6static 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 );
12static INSTALLSTATE WINAPI MsiComponentSearchTest_MsiLocateComponentW(
13 __in LPCWSTR szComponent,
14 __out_ecount_opt(*pcchBuf) LPWSTR lpPathBuf,
15 __inout_opt LPDWORD pcchBuf
16 );
17static 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 );
23static 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
32using namespace System;
33using namespace Xunit;
34using namespace Microsoft::Win32;
35
36namespace Microsoft
37{
38namespace Tools
39{
40namespace WindowsInstallerXml
41{
42namespace Test
43{
44namespace 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
524static 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
546static 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
614static 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
632static 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}
diff --git a/src/test/BurnUnitTest/VariableHelpers.cpp b/src/test/BurnUnitTest/VariableHelpers.cpp
new file mode 100644
index 00000000..9ce46a76
--- /dev/null
+++ b/src/test/BurnUnitTest/VariableHelpers.cpp
@@ -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
6using namespace System;
7using namespace Xunit;
8
9
10namespace Microsoft
11{
12namespace Tools
13{
14namespace WindowsInstallerXml
15{
16namespace Test
17{
18namespace 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}
diff --git a/src/test/BurnUnitTest/VariableHelpers.h b/src/test/BurnUnitTest/VariableHelpers.h
new file mode 100644
index 00000000..98c52649
--- /dev/null
+++ b/src/test/BurnUnitTest/VariableHelpers.h
@@ -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
5namespace Microsoft
6{
7namespace Tools
8{
9namespace WindowsInstallerXml
10{
11namespace Test
12{
13namespace Bootstrapper
14{
15
16
17void VariableSetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LPCWSTR wzValue);
18void VariableSetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, LONGLONG llValue);
19void VariableSetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable, DWORD64 qwValue);
20System::String^ VariableGetStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
21__int64 VariableGetNumericHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
22unsigned __int64 VariableGetVersionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
23System::String^ VariableGetFormattedHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
24System::String^ VariableFormatStringHelper(BURN_VARIABLES* pVariables, LPCWSTR wzIn);
25System::String^ VariableEscapeStringHelper(LPCWSTR wzIn);
26bool EvaluateConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
27bool EvaluateFailureConditionHelper(BURN_VARIABLES* pVariables, LPCWSTR wzCondition);
28bool VariableExistsHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
29int VariableGetTypeHelper(BURN_VARIABLES* pVariables, LPCWSTR wzVariable);
30
31
32}
33}
34}
35}
36}
diff --git a/src/test/BurnUnitTest/VariableTest.cpp b/src/test/BurnUnitTest/VariableTest.cpp
new file mode 100644
index 00000000..3a4caecf
--- /dev/null
+++ b/src/test/BurnUnitTest/VariableTest.cpp
@@ -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
7namespace Microsoft
8{
9namespace Tools
10{
11namespace WindowsInstallerXml
12{
13namespace Test
14{
15namespace 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}
diff --git a/src/test/BurnUnitTest/precomp.cpp b/src/test/BurnUnitTest/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/test/BurnUnitTest/precomp.cpp
@@ -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"
diff --git a/src/test/BurnUnitTest/precomp.h b/src/test/BurnUnitTest/precomp.h
new file mode 100644
index 00000000..1f2ccb8b
--- /dev/null
+++ b/src/test/BurnUnitTest/precomp.h
@@ -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"