aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-07-11 21:11:30 +1000
committerSean Hall <r.sean.hall@gmail.com>2020-07-12 11:51:29 +1000
commit9c4b5559ccb55491fe68b0096d1be0496fd6fcc7 (patch)
treefaa51204fc2e6e3dfd1b00ae03c7184c4fb0b4bf
parentf651268309263fa67fa84caf9fb6dbebb5c900d9 (diff)
downloadwix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.tar.gz
wix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.tar.bz2
wix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.zip
Import DUtilUnitTest from old wix4 repo.
-rw-r--r--src/test/DUtilUnitTest/AssemblyInfo.cpp12
-rw-r--r--src/test/DUtilUnitTest/CondUtilTest.cpp190
-rw-r--r--src/test/DUtilUnitTest/DUtilUnitTest.vcxproj73
-rw-r--r--src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters77
-rw-r--r--src/test/DUtilUnitTest/DictUtilTest.cpp187
-rw-r--r--src/test/DUtilUnitTest/DirUtilTests.cpp70
-rw-r--r--src/test/DUtilUnitTest/FileUtilTest.cpp118
-rw-r--r--src/test/DUtilUnitTest/GuidUtilTest.cpp60
-rw-r--r--src/test/DUtilUnitTest/IniUtilTest.cpp342
-rw-r--r--src/test/DUtilUnitTest/MemUtilTest.cpp491
-rw-r--r--src/test/DUtilUnitTest/MonUtilTest.cpp487
-rw-r--r--src/test/DUtilUnitTest/NativeAssert.h83
-rw-r--r--src/test/DUtilUnitTest/PathUtilTest.cpp80
-rw-r--r--src/test/DUtilUnitTest/SceUtilTest.cpp488
-rw-r--r--src/test/DUtilUnitTest/StrUtilTest.cpp184
-rw-r--r--src/test/DUtilUnitTest/UnitTest.rc7
-rw-r--r--src/test/DUtilUnitTest/UriUtilTest.cpp96
-rw-r--r--src/test/DUtilUnitTest/VarHelpers.cpp147
-rw-r--r--src/test/DUtilUnitTest/VarHelpers.h20
-rw-r--r--src/test/DUtilUnitTest/VarUtilTest.cpp532
-rw-r--r--src/test/DUtilUnitTest/error.h8
-rw-r--r--src/test/DUtilUnitTest/packages.config7
-rw-r--r--src/test/DUtilUnitTest/precomp.cpp3
-rw-r--r--src/test/DUtilUnitTest/precomp.h30
-rw-r--r--src/test/DUtilUnitTest/resource.h2
25 files changed, 3794 insertions, 0 deletions
diff --git a/src/test/DUtilUnitTest/AssemblyInfo.cpp b/src/test/DUtilUnitTest/AssemblyInfo.cpp
new file mode 100644
index 00000000..2d527910
--- /dev/null
+++ b/src/test/DUtilUnitTest/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 Dutil unit tests")];
10[assembly: AssemblyDescriptionAttribute("Dutil unit tests")];
11[assembly: AssemblyCultureAttribute("")];
12[assembly: ComVisible(false)];
diff --git a/src/test/DUtilUnitTest/CondUtilTest.cpp b/src/test/DUtilUnitTest/CondUtilTest.cpp
new file mode 100644
index 00000000..c808363d
--- /dev/null
+++ b/src/test/DUtilUnitTest/CondUtilTest.cpp
@@ -0,0 +1,190 @@
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 DutilTests
6{
7 using namespace System;
8 using namespace Xunit;
9 using namespace WixTest;
10
11 public ref class CondUtil
12 {
13 public:
14 [NamedFact(Skip = "condutil Not Implemented Yet.")]
15 void CondEvaluateTest()
16 {
17 HRESULT hr = S_OK;
18 VARIABLES_HANDLE pVariables = NULL;
19
20 try
21 {
22 hr = VarCreate(&pVariables);
23 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
24
25 // set variables
26 VarSetStringHelper(pVariables, L"PROP1", L"VAL1");
27 VarSetStringHelper(pVariables, L"PROP2", L"VAL2");
28 VarSetStringHelper(pVariables, L"PROP3", L"VAL3");
29 VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END");
30 VarSetNumericHelper(pVariables, L"PROP5", 5);
31 VarSetNumericHelper(pVariables, L"PROP6", 6);
32 VarSetStringHelper(pVariables, L"PROP7", L"");
33 VarSetNumericHelper(pVariables, L"PROP8", 0);
34 VarSetStringHelper(pVariables, L"_PROP9", L"VAL9");
35 VarSetNumericHelper(pVariables, L"PROP10", -10);
36 VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll);
37 VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll);
38 VarSetNumericHelper(pVariables, L"PROP13", 0x00010000);
39 VarSetNumericHelper(pVariables, L"PROP14", 0x00000001);
40 VarSetNumericHelper(pVariables, L"PROP15", 0x00010001);
41 VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0));
42 VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0));
43 VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0));
44 VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0));
45 VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1));
46 VarSetNumericHelper(pVariables, L"vPROP21", 1);
47 VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535));
48 VarSetStringHelper(pVariables, L"PROP23", L"1.1.1");
49
50 // test conditions
51 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1"));
52 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5"));
53 Assert::False(EvaluateConditionHelper(pVariables, L"PROP7"));
54 Assert::False(EvaluateConditionHelper(pVariables, L"PROP8"));
55 Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9"));
56 Assert::False(EvaluateConditionHelper(pVariables, L"PROP16"));
57 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17"));
58
59 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\""));
60 Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\""));
61 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\""));
62 Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\""));
63
64 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\""));
65 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\""));
66 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\""));
67 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\""));
68
69 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5"));
70 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0"));
71 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5"));
72 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0"));
73
74 Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10"));
75 Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10"));
76
77 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1"));
78 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0"));
79 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1"));
80 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0"));
81
82 Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0"));
83 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1"));
84 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1"));
85 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1"));
86 Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1"));
87 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0"));
88 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1"));
89 Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1"));
90 Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1"));
91 Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23"));
92 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1"));
93 Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1"));
94
95 Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806"));
96 Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807"));
97 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808"));
98 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000"));
99
100 Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807"));
101 Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808"));
102 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809"));
103 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000"));
104
105 Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535"));
106 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535"));
107 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535"));
108
109 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6"));
110 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5"));
111 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4"));
112 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5"));
113 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6"));
114 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5"));
115 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4"));
116 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4"));
117 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5"));
118 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6"));
119
120 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\""));
121 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\""));
122 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\""));
123 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\""));
124 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\""));
125 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\""));
126
127 Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1"));
128 Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0"));
129 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12"));
130 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1"));
131 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0"));
132 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1"));
133 Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1"));
134 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1"));
135 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1"));
136 Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123"));
137
138 Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\""));
139 Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6"));
140 Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\""));
141 Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6"));
142 Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6"));
143
144 Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1"));
145 Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0"));
146 Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1"));
147 Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0"));
148 Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537"));
149 Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0"));
150
151 Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1"));
152 Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")"));
153
154 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
155 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
156 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\""));
157 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\""));
158
159 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\""));
160 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\""));
161 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\""));
162 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\""));
163
164 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\""));
165 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\""));
166 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\""));
167 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")"));
168 Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\""));
169
170 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
171 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
172 Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
173 Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\""));
174 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")"));
175
176 Assert::True(EvaluateFailureConditionHelper(pVariables, L"="));
177 Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1"));
178 Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \""));
179 Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A"));
180 Assert::True(EvaluateFailureConditionHelper(pVariables, L"*"));
181
182 Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1"));
183 }
184 finally
185 {
186 ReleaseVariables(pVariables);
187 }
188 }
189 };
190}
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
new file mode 100644
index 00000000..292cf28a
--- /dev/null
+++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
@@ -0,0 +1,73 @@
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>{AB7EE608-E5FB-42A5-831F-0DEEEA141223}</ProjectGuid>
19 <RootNamespace>DUtilUnitTests</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</ProjectAdditionalIncludeDirectories>
28 <ProjectAdditionalLinkLibraries>rpcrt4.lib;dutil.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries>
29 </PropertyGroup>
30 <ItemGroup>
31 <ClCompile Include="AssemblyInfo.cpp" />
32 <ClCompile Include="CondUtilTest.cpp" />
33 <ClCompile Include="DictUtilTest.cpp" />
34 <ClCompile Include="DirUtilTests.cpp" />
35 <ClCompile Include="FileUtilTest.cpp" />
36 <ClCompile Include="GuidUtilTest.cpp" />
37 <ClCompile Include="IniUtilTest.cpp" />
38 <ClCompile Include="MemUtilTest.cpp" />
39 <ClCompile Include="MonUtilTest.cpp" />
40 <ClCompile Include="PathUtilTest.cpp" />
41 <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
42 <ClCompile Include="StrUtilTest.cpp" />
43 <ClCompile Include="UriUtilTest.cpp" />
44 <ClCompile Include="VarHelpers.cpp" />
45 <ClCompile Include="VarUtilTest.cpp" />
46 </ItemGroup>
47 <ItemGroup>
48 <ClInclude Include="precomp.h" />
49 <ClInclude Include="error.h" />
50 <ClInclude Include="VarHelpers.h" />
51 </ItemGroup>
52 <ItemGroup>
53 <ResourceCompile Include="UnitTest.rc" />
54 </ItemGroup>
55 <ItemGroup>
56 <Reference Include="System" />
57 <Reference Include="xunit">
58 <HintPath>$(XunitPath)\xunit.dll</HintPath>
59 </Reference>
60 </ItemGroup>
61 <ItemGroup>
62 <ProjectReference Include="..\..\..\..\src\libs\dutil\dutil.vcxproj" />
63 <ProjectReference Include="..\..\WixCppCliTestTools\WixCppCliTestTools.vcxproj">
64 <Project>{95BABD97-FBDB-453A-AF8A-FA031A07B599}</Project>
65 <Name>WixCppCliTestTools</Name>
66 </ProjectReference>
67 <ProjectReference Include="..\..\WixTestTools\WixTestTools.csproj">
68 <Project>{55CB1042-647B-4347-9876-3EA607AF8DCE}</Project>
69 <Name>WixTestTools</Name>
70 </ProjectReference>
71 </ItemGroup>
72 <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" />
73</Project>
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
new file mode 100644
index 00000000..a83db35d
--- /dev/null
+++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
@@ -0,0 +1,77 @@
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;mfcribbon-ms</Extensions>
15 </Filter>
16 </ItemGroup>
17 <ItemGroup>
18 <ClCompile Include="AssemblyInfo.cpp">
19 <Filter>Source Files</Filter>
20 </ClCompile>
21 <ClCompile Include="CondUtilTest.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 <ClCompile Include="DictUtilTest.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
27 <ClCompile Include="DirUtilTests.cpp">
28 <Filter>Source Files</Filter>
29 </ClCompile>
30 <ClCompile Include="FileUtilTest.cpp">
31 <Filter>Source Files</Filter>
32 </ClCompile>
33 <ClCompile Include="GuidUtilTest.cpp">
34 <Filter>Source Files</Filter>
35 </ClCompile>
36 <ClCompile Include="IniUtilTest.cpp">
37 <Filter>Source Files</Filter>
38 </ClCompile>
39 <ClCompile Include="MemUtilTest.cpp">
40 <Filter>Source Files</Filter>
41 </ClCompile>
42 <ClCompile Include="MonUtilTest.cpp">
43 <Filter>Source Files</Filter>
44 </ClCompile>
45 <ClCompile Include="PathUtilTest.cpp">
46 <Filter>Source Files</Filter>
47 </ClCompile>
48 <ClCompile Include="StrUtilTest.cpp">
49 <Filter>Source Files</Filter>
50 </ClCompile>
51 <ClCompile Include="UriUtilTest.cpp">
52 <Filter>Source Files</Filter>
53 </ClCompile>
54 <ClCompile Include="VarHelpers.cpp">
55 <Filter>Source Files</Filter>
56 </ClCompile>
57 <ClCompile Include="VarUtilTest.cpp">
58 <Filter>Source Files</Filter>
59 </ClCompile>
60 </ItemGroup>
61 <ItemGroup>
62 <ResourceCompile Include="UnitTest.rc">
63 <Filter>Resource Files</Filter>
64 </ResourceCompile>
65 </ItemGroup>
66 <ItemGroup>
67 <ClInclude Include="precomp.h">
68 <Filter>Header Files</Filter>
69 </ClInclude>
70 <ClInclude Include="error.h">
71 <Filter>Header Files</Filter>
72 </ClInclude>
73 <ClInclude Include="VarHelpers.h">
74 <Filter>Header Files</Filter>
75 </ClInclude>
76 </ItemGroup>
77</Project> \ No newline at end of file
diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp
new file mode 100644
index 00000000..fd8a5953
--- /dev/null
+++ b/src/test/DUtilUnitTest/DictUtilTest.cpp
@@ -0,0 +1,187 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9const DWORD numIterations = 100000;
10
11namespace DutilTests
12{
13 struct Value
14 {
15 DWORD dwNum;
16 LPWSTR sczKey;
17 };
18
19 public ref class DictUtil
20 {
21 public:
22 [Fact]
23 void DictUtilTest()
24 {
25 EmbeddedKeyTestHelper(DICT_FLAG_NONE, numIterations);
26
27 EmbeddedKeyTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations);
28
29 StringListTestHelper(DICT_FLAG_NONE, numIterations);
30
31 StringListTestHelper(DICT_FLAG_CASEINSENSITIVE, numIterations);
32 }
33
34 private:
35 void EmbeddedKeyTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations)
36 {
37 HRESULT hr = S_OK;
38 Value *rgValues = NULL;
39 Value *valueFound = NULL;
40 DWORD cValues = 0;
41 LPWSTR sczExpectedKey = NULL;
42 STRINGDICT_HANDLE sdValues = NULL;
43
44 try
45 {
46 hr = DictCreateWithEmbeddedKey(&sdValues, 0, (void **)&rgValues, offsetof(Value, sczKey), dfFlags);
47 NativeAssert::Succeeded(hr, "Failed to create dictionary of values");
48
49 for (DWORD i = 0; i < dwNumIterations; ++i)
50 {
51 cValues++;
52
53 hr = MemEnsureArraySize((void **)&rgValues, cValues, sizeof(Value), 5);
54 NativeAssert::Succeeded(hr, "Failed to grow value array");
55
56 hr = StrAllocFormatted(&rgValues[i].sczKey, L"%u_a_%u", i, i);
57 NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i);
58
59 hr = DictAddValue(sdValues, rgValues + i);
60 NativeAssert::Succeeded(hr, "Failed to add item {0} to dict", i);
61 }
62
63 for (DWORD i = 0; i < dwNumIterations; ++i)
64 {
65 hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i);
66 NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i);
67
68 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
69 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
70
71 NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey);
72
73 hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i);
74 NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i);
75
76 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
77
78 if (dfFlags & DICT_FLAG_CASEINSENSITIVE)
79 {
80 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
81
82 NativeAssert::StringEqual(sczExpectedKey, valueFound->sczKey, TRUE);
83 }
84 else
85 {
86 if (E_NOTFOUND != hr)
87 {
88 hr = E_FAIL;
89 ExitOnFailure(hr, "This embedded key is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey);
90 }
91 }
92
93 hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i);
94 NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i);
95
96 hr = DictGetValue(sdValues, sczExpectedKey, (void **)&valueFound);
97 if (E_NOTFOUND != hr)
98 {
99 hr = E_FAIL;
100 ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey);
101 }
102 }
103 }
104 finally
105 {
106 for (DWORD i = 0; i < cValues; ++i)
107 {
108 ReleaseStr(rgValues[i].sczKey);
109 }
110 ReleaseMem(rgValues);
111 ReleaseStr(sczExpectedKey);
112 ReleaseDict(sdValues);
113 }
114
115 LExit:
116 return;
117 }
118
119 void StringListTestHelper(DICT_FLAG dfFlags, DWORD dwNumIterations)
120 {
121 HRESULT hr = S_OK;
122 LPWSTR sczKey = NULL;
123 LPWSTR sczExpectedKey = NULL;
124 STRINGDICT_HANDLE sdValues = NULL;
125
126 try
127 {
128 hr = DictCreateStringList(&sdValues, 0, dfFlags);
129 NativeAssert::Succeeded(hr, "Failed to create dictionary of keys");
130
131 for (DWORD i = 0; i < dwNumIterations; ++i)
132 {
133 hr = StrAllocFormatted(&sczKey, L"%u_a_%u", i, i);
134 NativeAssert::Succeeded(hr, "Failed to allocate key for value {0}", i);
135
136 hr = DictAddKey(sdValues, sczKey);
137 NativeAssert::Succeeded(hr, "Failed to add key {0} to dict", i);
138 }
139
140 for (DWORD i = 0; i < dwNumIterations; ++i)
141 {
142 hr = StrAllocFormatted(&sczExpectedKey, L"%u_a_%u", i, i);
143 NativeAssert::Succeeded(hr, "Failed to allocate expected key {0}", i);
144
145 hr = DictKeyExists(sdValues, sczExpectedKey);
146 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
147
148 hr = StrAllocFormatted(&sczExpectedKey, L"%u_A_%u", i, i);
149 NativeAssert::Succeeded(hr, "Failed to allocate uppercase expected key {0}", i);
150
151 hr = DictKeyExists(sdValues, sczExpectedKey);
152 if (dfFlags & DICT_FLAG_CASEINSENSITIVE)
153 {
154 NativeAssert::Succeeded(hr, "Failed to find value {0}", sczExpectedKey);
155 }
156 else
157 {
158 if (E_NOTFOUND != hr)
159 {
160 hr = E_FAIL;
161 ExitOnFailure(hr, "This stringlist dict is case sensitive, but it seemed to have found something case using case insensitivity!: %ls", sczExpectedKey);
162 }
163 }
164
165 hr = StrAllocFormatted(&sczExpectedKey, L"%u_b_%u", i, i);
166 NativeAssert::Succeeded(hr, "Failed to allocate unexpected key {0}", i);
167
168 hr = DictKeyExists(sdValues, sczExpectedKey);
169 if (E_NOTFOUND != hr)
170 {
171 hr = E_FAIL;
172 ExitOnFailure(hr, "Item shouldn't have been found in dictionary: %ls", sczExpectedKey);
173 }
174 }
175 }
176 finally
177 {
178 ReleaseStr(sczKey);
179 ReleaseStr(sczExpectedKey);
180 ReleaseDict(sdValues);
181 }
182
183 LExit:
184 return;
185 }
186 };
187}
diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp
new file mode 100644
index 00000000..a965c3d5
--- /dev/null
+++ b/src/test/DUtilUnitTest/DirUtilTests.cpp
@@ -0,0 +1,70 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 public ref class DirUtil
12 {
13 public:
14 [Fact]
15 void DirUtilTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczCurrentDir = NULL;
19 LPWSTR sczGuid = NULL;
20 LPWSTR sczFolder = NULL;
21 LPWSTR sczSubFolder = NULL;
22
23 try
24 {
25 hr = GuidCreate(&sczGuid);
26 NativeAssert::Succeeded(hr, "Failed to create guid.");
27
28 hr = DirGetCurrent(&sczCurrentDir);
29 NativeAssert::Succeeded(hr, "Failed to get current directory.");
30
31 hr = PathConcat(sczCurrentDir, sczGuid, &sczFolder);
32 NativeAssert::Succeeded(hr, "Failed to combine current directory: '{0}' with Guid: '{1}'", sczCurrentDir, sczGuid);
33
34 BOOL fExists = DirExists(sczFolder, NULL);
35 Assert::False(fExists);
36
37 hr = PathConcat(sczFolder, L"foo", &sczSubFolder);
38 NativeAssert::Succeeded(hr, "Failed to combine folder: '%ls' with subfolder: 'foo'", sczFolder);
39
40 hr = DirEnsureExists(sczSubFolder, NULL);
41 NativeAssert::Succeeded(hr, "Failed to create multiple directories: %ls", sczSubFolder);
42
43 // Test failure to delete non-empty folder.
44 hr = DirEnsureDelete(sczFolder, FALSE, FALSE);
45 Assert::Equal(HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY), hr);
46
47 hr = DirEnsureDelete(sczSubFolder, FALSE, FALSE);
48 NativeAssert::Succeeded(hr, "Failed to delete single directory: %ls", sczSubFolder);
49
50 // Put the directory back and we'll test deleting tree.
51 hr = DirEnsureExists(sczSubFolder, NULL);
52 NativeAssert::Succeeded(hr, "Failed to create single directory: %ls", sczSubFolder);
53
54 hr = DirEnsureDelete(sczFolder, FALSE, TRUE);
55 NativeAssert::Succeeded(hr, "Failed to delete directory tree: %ls", sczFolder);
56
57 // Finally, try to create "C:\" which would normally fail, but we want success
58 hr = DirEnsureExists(L"C:\\", NULL);
59 NativeAssert::Succeeded(hr, "Failed to create C:\\");
60 }
61 finally
62 {
63 ReleaseStr(sczSubFolder);
64 ReleaseStr(sczFolder);
65 ReleaseStr(sczGuid);
66 ReleaseStr(sczCurrentDir);
67 }
68 }
69 };
70}
diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp
new file mode 100644
index 00000000..41638bdb
--- /dev/null
+++ b/src/test/DUtilUnitTest/FileUtilTest.cpp
@@ -0,0 +1,118 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 public ref class FileUtil
12 {
13 public:
14 [Fact(Skip="Skipped until we have a good way to reference ANSI.txt.")]
15 void FileUtilTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczTempDir = NULL;
19 LPWSTR sczFileDir = NULL;
20
21 try
22 {
23 hr = PathExpand(&sczTempDir, L"%TEMP%\\FileUtilTest\\", PATH_EXPAND_ENVIRONMENT);
24 NativeAssert::Succeeded(hr, "Failed to get temp dir");
25
26 hr = PathExpand(&sczFileDir, L"%WIX_ROOT%\\examples\\data\\TextEncodings\\", PATH_EXPAND_ENVIRONMENT);
27 NativeAssert::Succeeded(hr, "Failed to get path to encodings file dir");
28
29 hr = DirEnsureExists(sczTempDir, NULL);
30 NativeAssert::Succeeded(hr, "Failed to ensure directory exists: {0}", sczTempDir);
31
32 TestFile(sczFileDir, sczTempDir, L"ANSI.txt", 32, FILE_ENCODING_UTF8);
33 // Big endian not supported today!
34 //TestFile(sczFileDir, L"UnicodeBENoBOM.txt", 34);
35 //TestFile(sczFileDir, L"UnicodeBEWithBOM.txt", 34);
36 TestFile(sczFileDir, sczTempDir, L"UnicodeLENoBOM.txt", 34, FILE_ENCODING_UTF16);
37 TestFile(sczFileDir, sczTempDir, L"UnicodeLEWithBOM.txt", 34, FILE_ENCODING_UTF16_WITH_BOM);
38 TestFile(sczFileDir, sczTempDir, L"UTF8WithSignature.txt", 34, FILE_ENCODING_UTF8_WITH_BOM);
39
40 hr = DirEnsureDelete(sczTempDir, TRUE, TRUE);
41 }
42 finally
43 {
44 ReleaseStr(sczTempDir);
45 ReleaseStr(sczFileDir);
46 }
47 }
48
49 private:
50 void TestFile(LPWSTR wzDir, LPCWSTR wzTempDir, LPWSTR wzFileName, DWORD dwExpectedStringLength, FILE_ENCODING feExpectedEncoding)
51 {
52 HRESULT hr = S_OK;
53 LPWSTR sczFullPath = NULL;
54 LPWSTR sczContents = NULL;
55 LPWSTR sczOutputPath = NULL;
56 FILE_ENCODING feEncodingFound = FILE_ENCODING_UNSPECIFIED;
57 BYTE *pbFile1 = NULL;
58 DWORD cbFile1 = 0;
59 BYTE *pbFile2 = NULL;
60 DWORD cbFile2 = 0;
61
62 try
63 {
64 hr = PathConcat(wzDir, wzFileName, &sczFullPath);
65 NativeAssert::Succeeded(hr, "Failed to create path to test file: {0}", sczFullPath);
66
67 hr = FileToString(sczFullPath, &sczContents, &feEncodingFound);
68 hr = E_FAIL;
69 NativeAssert::Succeeded(hr, "Failed to read text from file: {0}", sczFullPath);
70
71 if (!sczContents)
72 {
73 hr = E_FAIL;
74 NativeAssert::Succeeded(hr, "FileToString() returned NULL for file: {0}", sczFullPath);
75 }
76
77 if ((DWORD)lstrlenW(sczContents) != dwExpectedStringLength)
78 {
79 hr = E_FAIL;
80 ExitOnFailure(hr, "FileToString() returned wrong size for file: %ls (expected size %u, found size %u)", sczFullPath, dwExpectedStringLength, lstrlenW(sczContents));
81 }
82
83 if (feEncodingFound != feExpectedEncoding)
84 {
85 hr = E_FAIL;
86 ExitOnFailure(hr, "FileToString() returned unexpected encoding type for file: %ls (expected type %u, found type %u)", sczFullPath, feExpectedEncoding, feEncodingFound);
87 }
88
89 hr = PathConcat(wzTempDir, wzFileName, &sczOutputPath);
90 NativeAssert::Succeeded(hr, "Failed to get output path");
91
92 hr = FileFromString(sczOutputPath, 0, sczContents, feExpectedEncoding);
93 NativeAssert::Succeeded(hr, "Failed to write contents of file back out to disk");
94
95 hr = FileRead(&pbFile1, &cbFile1, sczFullPath);
96 NativeAssert::Succeeded(hr, "Failed to read input file as binary");
97
98 hr = FileRead(&pbFile2, &cbFile2, sczOutputPath);
99 NativeAssert::Succeeded(hr, "Failed to read output file as binary");
100
101 if (cbFile1 != cbFile2 || 0 != memcmp(pbFile1, pbFile2, cbFile1))
102 {
103 hr = E_FAIL;
104 ExitOnFailure(hr, "Outputted file doesn't match input file: \"%ls\" and \"%ls\"", sczFullPath, sczOutputPath);
105 }
106 }
107 finally
108 {
109 ReleaseStr(sczOutputPath);
110 ReleaseStr(sczFullPath);
111 ReleaseStr(sczContents);
112 }
113
114 LExit:
115 return;
116 }
117 };
118}
diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp
new file mode 100644
index 00000000..d0ea9a89
--- /dev/null
+++ b/src/test/DUtilUnitTest/GuidUtilTest.cpp
@@ -0,0 +1,60 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 public ref class GuidUtil
12 {
13 public:
14 [Fact]
15 void GuidCreateTest()
16 {
17 HRESULT hr = S_OK;
18 WCHAR wzGuid1[GUID_STRING_LENGTH];
19 WCHAR wzGuid2[GUID_STRING_LENGTH];
20
21 hr = GuidFixedCreate(wzGuid1);
22 NativeAssert::Succeeded(hr, "Failed to create first guid.");
23 Guid firstGuid = Guid::Parse(gcnew String(wzGuid1));
24
25 hr = GuidFixedCreate(wzGuid2);
26 NativeAssert::Succeeded(hr, "Failed to create second guid.");
27 Guid secondGuid = Guid::Parse(gcnew String(wzGuid2));
28
29 NativeAssert::NotStringEqual(wzGuid1, wzGuid2);
30 NativeAssert::NotEqual(firstGuid, secondGuid);
31 }
32
33 [Fact]
34 void GuidCreateSczTest()
35 {
36 HRESULT hr = S_OK;
37 LPWSTR sczGuid1 = NULL;
38 LPWSTR sczGuid2 = NULL;
39
40 try
41 {
42 hr = GuidCreate(&sczGuid1);
43 NativeAssert::Succeeded(hr, "Failed to create first guid.");
44 Guid firstGuid = Guid::Parse(gcnew String(sczGuid1));
45
46 hr = GuidCreate(&sczGuid2);
47 NativeAssert::Succeeded(hr, "Failed to create second guid.");
48 Guid secondGuid = Guid::Parse(gcnew String(sczGuid2));
49
50 NativeAssert::NotStringEqual(sczGuid1, sczGuid2);
51 NativeAssert::NotEqual(firstGuid, secondGuid);
52 }
53 finally
54 {
55 ReleaseStr(sczGuid1);
56 ReleaseStr(sczGuid2);
57 }
58 }
59 };
60}
diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp
new file mode 100644
index 00000000..e28f357e
--- /dev/null
+++ b/src/test/DUtilUnitTest/IniUtilTest.cpp
@@ -0,0 +1,342 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9typedef HRESULT (__clrcall *IniFormatParameters)(
10 INI_HANDLE
11 );
12
13namespace DutilTests
14{
15 public ref class IniUtil
16 {
17 public:
18 [Fact]
19 void IniUtilTest()
20 {
21 HRESULT hr = S_OK;
22 LPWSTR sczTempIniFilePath = NULL;
23 LPWSTR sczTempIniFileDir = NULL;
24 LPWSTR wzIniContents = L" PlainValue = \t Blah \r\n;CommentHere\r\n[Section1]\r\n ;Another Comment With = Equal Sign\r\nSection1ValueA=Foo\r\n\r\nSection1ValueB=Bar\r\n[Section2]\r\nSection2ValueA=Cha\r\nArray[0]=Arr\r\n";
25 LPWSTR wzScriptContents = L"setf ~PlainValue Blah\r\n;CommentHere\r\n\r\nsetf ~Section1\\Section1ValueA Foo\r\n\r\nsetf ~Section1\\Section1ValueB Bar\r\nsetf ~Section2\\Section2ValueA Cha\r\nsetf ~Section2\\Array[0] Arr\r\n";
26
27 try
28 {
29 hr = PathExpand(&sczTempIniFilePath, L"%TEMP%\\IniUtilTest\\Test.ini", PATH_EXPAND_ENVIRONMENT);
30 NativeAssert::Succeeded(hr, "Failed to get path to temp INI file");
31
32 hr = PathGetDirectory(sczTempIniFilePath, &sczTempIniFileDir);
33 NativeAssert::Succeeded(hr, "Failed to get directory to temp INI file");
34
35 hr = DirEnsureDelete(sczTempIniFileDir, TRUE, TRUE);
36 if (E_PATHNOTFOUND == hr)
37 {
38 hr = S_OK;
39 }
40 NativeAssert::Succeeded(hr, "Failed to delete IniUtilTest directory: {0}", sczTempIniFileDir);
41
42 hr = DirEnsureExists(sczTempIniFileDir, NULL);
43 NativeAssert::Succeeded(hr, "Failed to ensure temp directory exists: {0}", sczTempIniFileDir);
44
45 // Tests parsing, then modifying a regular INI file
46 TestReadThenWrite(sczTempIniFilePath, StandardIniFormat, wzIniContents);
47
48 // Tests programmatically creating from scratch, then parsing an INI file
49 TestWriteThenRead(sczTempIniFilePath, StandardIniFormat);
50
51 // Tests parsing, then modifying a regular INI file
52 TestReadThenWrite(sczTempIniFilePath, ScriptFormat, wzScriptContents);
53
54 // Tests programmatically creating from scratch, then parsing an INI file
55 TestWriteThenRead(sczTempIniFilePath, ScriptFormat);
56 }
57 finally
58 {
59 ReleaseStr(sczTempIniFilePath);
60 ReleaseStr(sczTempIniFileDir);
61 }
62 }
63
64 private:
65 void AssertValue(INI_HANDLE iniHandle, LPCWSTR wzValueName, LPCWSTR wzValue)
66 {
67 HRESULT hr = S_OK;
68 LPWSTR sczValue = NULL;
69
70 try
71 {
72 hr = IniGetValue(iniHandle, wzValueName, &sczValue);
73 NativeAssert::Succeeded(hr, "Failed to get ini value: {0}", wzValueName);
74
75 if (0 != wcscmp(sczValue, wzValue))
76 {
77 hr = E_FAIL;
78 ExitOnFailure(hr, "Expected to find value in INI: '%ls'='%ls' - but found value '%ls' instead", wzValueName, wzValue, sczValue);
79 }
80 }
81 finally
82 {
83 ReleaseStr(sczValue);
84 }
85
86 LExit:
87 return;
88 }
89
90 void AssertNoValue(INI_HANDLE iniHandle, LPCWSTR wzValueName)
91 {
92 HRESULT hr = S_OK;
93 LPWSTR sczValue = NULL;
94
95 try
96 {
97 hr = IniGetValue(iniHandle, wzValueName, &sczValue);
98 if (E_NOTFOUND != hr)
99 {
100 if (SUCCEEDED(hr))
101 {
102 hr = E_FAIL;
103 }
104 ExitOnFailure(hr, "INI value shouldn't have been found: %ls", wzValueName);
105 }
106 }
107 finally
108 {
109 ReleaseStr(sczValue);
110 }
111
112 LExit:
113 return;
114 }
115
116 static HRESULT StandardIniFormat(__inout INI_HANDLE iniHandle)
117 {
118 HRESULT hr = S_OK;
119
120 hr = IniSetOpenTag(iniHandle, L"[", L"]");
121 NativeAssert::Succeeded(hr, "Failed to set open tag settings on ini handle");
122
123 hr = IniSetValueStyle(iniHandle, NULL, L"=");
124 NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle");
125
126 hr = IniSetCommentStyle(iniHandle, L";");
127 NativeAssert::Succeeded(hr, "Failed to set comment style setting on ini handle");
128
129 return hr;
130 }
131
132 static HRESULT ScriptFormat(__inout INI_HANDLE iniHandle)
133 {
134 HRESULT hr = S_OK;
135
136 hr = IniSetValueStyle(iniHandle, L"setf ~", L" ");
137 NativeAssert::Succeeded(hr, "Failed to set value separator setting on ini handle");
138
139 return hr;
140 }
141
142 void TestReadThenWrite(LPWSTR wzIniFilePath, IniFormatParameters SetFormat, LPCWSTR wzContents)
143 {
144 HRESULT hr = S_OK;
145 INI_HANDLE iniHandle = NULL;
146 INI_HANDLE iniHandle2 = NULL;
147 INI_VALUE *rgValues = NULL;
148 DWORD cValues = 0;
149
150 try
151 {
152 hr = FileWrite(wzIniFilePath, 0, reinterpret_cast<LPCBYTE>(wzContents), lstrlenW(wzContents) * sizeof(WCHAR), NULL);
153 NativeAssert::Succeeded(hr, "Failed to write out INI file");
154
155 hr = IniInitialize(&iniHandle);
156 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
157
158 hr = SetFormat(iniHandle);
159 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
160
161 hr = IniParse(iniHandle, wzIniFilePath, NULL);
162 NativeAssert::Succeeded(hr, "Failed to parse INI file");
163
164 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
165 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
166
167 NativeAssert::Equal<DWORD>(5, cValues);
168
169 AssertValue(iniHandle, L"PlainValue", L"Blah");
170 AssertNoValue(iniHandle, L"PlainValue2");
171 AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo");
172 AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar");
173 AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha");
174 AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist");
175 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr");
176
177 hr = IniSetValue(iniHandle, L"PlainValue2", L"Blah2");
178 NativeAssert::Succeeded(hr, "Failed to set value in INI");
179
180 hr = IniSetValue(iniHandle, L"Section1\\CreatedValue", L"Woo");
181 NativeAssert::Succeeded(hr, "Failed to set value in INI");
182
183 hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arrmod");
184 NativeAssert::Succeeded(hr, "Failed to set value in INI");
185
186 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
187 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
188
189 NativeAssert::Equal<DWORD>(7, cValues);
190
191 AssertValue(iniHandle, L"PlainValue", L"Blah");
192 AssertValue(iniHandle, L"PlainValue2", L"Blah2");
193 AssertValue(iniHandle, L"Section1\\Section1ValueA", L"Foo");
194 AssertValue(iniHandle, L"Section1\\Section1ValueB", L"Bar");
195 AssertValue(iniHandle, L"Section2\\Section2ValueA", L"Cha");
196 AssertNoValue(iniHandle, L"Section1\\ValueDoesntExist");
197 AssertValue(iniHandle, L"Section1\\CreatedValue", L"Woo");
198 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arrmod");
199
200 // Try deleting a value as well
201 hr = IniSetValue(iniHandle, L"Section1\\Section1ValueB", NULL);
202 NativeAssert::Succeeded(hr, "Failed to kill value in INI");
203
204 hr = IniWriteFile(iniHandle, NULL, FILE_ENCODING_UNSPECIFIED);
205 NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk");
206
207 ReleaseNullIni(iniHandle);
208 // Now re-parse the INI we just wrote and make sure it matches the values we expect
209 hr = IniInitialize(&iniHandle2);
210 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
211
212 hr = SetFormat(iniHandle2);
213 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
214
215 hr = IniParse(iniHandle2, wzIniFilePath, NULL);
216 NativeAssert::Succeeded(hr, "Failed to parse INI file");
217
218 hr = IniGetValueList(iniHandle2, &rgValues, &cValues);
219 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
220
221 NativeAssert::Equal<DWORD>(6, cValues);
222
223 AssertValue(iniHandle2, L"PlainValue", L"Blah");
224 AssertValue(iniHandle2, L"PlainValue2", L"Blah2");
225 AssertValue(iniHandle2, L"Section1\\Section1ValueA", L"Foo");
226 AssertNoValue(iniHandle2, L"Section1\\Section1ValueB");
227 AssertValue(iniHandle2, L"Section2\\Section2ValueA", L"Cha");
228 AssertNoValue(iniHandle2, L"Section1\\ValueDoesntExist");
229 AssertValue(iniHandle2, L"Section1\\CreatedValue", L"Woo");
230 AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arrmod");
231 }
232 finally
233 {
234 ReleaseIni(iniHandle);
235 ReleaseIni(iniHandle2);
236 }
237 }
238
239 void TestWriteThenRead(LPWSTR wzIniFilePath, IniFormatParameters SetFormat)
240 {
241 HRESULT hr = S_OK;
242 INI_HANDLE iniHandle = NULL;
243 INI_HANDLE iniHandle2 = NULL;
244 INI_VALUE *rgValues = NULL;
245 DWORD cValues = 0;
246
247 try
248 {
249 hr = FileEnsureDelete(wzIniFilePath);
250 NativeAssert::Succeeded(hr, "Failed to ensure file is deleted");
251
252 hr = IniInitialize(&iniHandle);
253 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
254
255 hr = SetFormat(iniHandle);
256 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
257
258 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
259 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
260
261 NativeAssert::Equal<DWORD>(0, cValues);
262
263 hr = IniSetValue(iniHandle, L"Value1", L"BlahTypo");
264 NativeAssert::Succeeded(hr, "Failed to set value in INI");
265
266 hr = IniSetValue(iniHandle, L"Value2", L"Blah2");
267 NativeAssert::Succeeded(hr, "Failed to set value in INI");
268
269 hr = IniSetValue(iniHandle, L"Section1\\Value1", L"Section1Value1");
270 NativeAssert::Succeeded(hr, "Failed to set value in INI");
271
272 hr = IniSetValue(iniHandle, L"Section1\\Value2", L"Section1Value2");
273 NativeAssert::Succeeded(hr, "Failed to set value in INI");
274
275 hr = IniSetValue(iniHandle, L"Section2\\Value1", L"Section2Value1");
276 NativeAssert::Succeeded(hr, "Failed to set value in INI");
277
278 hr = IniSetValue(iniHandle, L"Section2\\Array[0]", L"Arr");
279 NativeAssert::Succeeded(hr, "Failed to set value in INI");
280
281 hr = IniSetValue(iniHandle, L"Value3", L"Blah3");
282 NativeAssert::Succeeded(hr, "Failed to set value in INI");
283
284 hr = IniSetValue(iniHandle, L"Value4", L"Blah4");
285 NativeAssert::Succeeded(hr, "Failed to set value in INI");
286
287 hr = IniSetValue(iniHandle, L"Value4", NULL);
288 NativeAssert::Succeeded(hr, "Failed to set value in INI");
289
290 hr = IniSetValue(iniHandle, L"Value1", L"Blah1");
291 NativeAssert::Succeeded(hr, "Failed to set value in INI");
292
293 hr = IniGetValueList(iniHandle, &rgValues, &cValues);
294 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
295
296 NativeAssert::Equal<DWORD>(8, cValues);
297
298 AssertValue(iniHandle, L"Value1", L"Blah1");
299 AssertValue(iniHandle, L"Value2", L"Blah2");
300 AssertValue(iniHandle, L"Value3", L"Blah3");
301 AssertNoValue(iniHandle, L"Value4");
302 AssertValue(iniHandle, L"Section1\\Value1", L"Section1Value1");
303 AssertValue(iniHandle, L"Section1\\Value2", L"Section1Value2");
304 AssertValue(iniHandle, L"Section2\\Value1", L"Section2Value1");
305 AssertValue(iniHandle, L"Section2\\Array[0]", L"Arr");
306
307 hr = IniWriteFile(iniHandle, wzIniFilePath, FILE_ENCODING_UNSPECIFIED);
308 NativeAssert::Succeeded(hr, "Failed to write ini file back out to disk");
309
310 ReleaseNullIni(iniHandle);
311 // Now re-parse the INI we just wrote and make sure it matches the values we expect
312 hr = IniInitialize(&iniHandle2);
313 NativeAssert::Succeeded(hr, "Failed to initialize INI object");
314
315 hr = SetFormat(iniHandle2);
316 NativeAssert::Succeeded(hr, "Failed to set parameters for INI file");
317
318 hr = IniParse(iniHandle2, wzIniFilePath, NULL);
319 NativeAssert::Succeeded(hr, "Failed to parse INI file");
320
321 hr = IniGetValueList(iniHandle2, &rgValues, &cValues);
322 NativeAssert::Succeeded(hr, "Failed to get list of values in INI");
323
324 NativeAssert::Equal<DWORD>(7, cValues);
325
326 AssertValue(iniHandle2, L"Value1", L"Blah1");
327 AssertValue(iniHandle2, L"Value2", L"Blah2");
328 AssertValue(iniHandle2, L"Value3", L"Blah3");
329 AssertNoValue(iniHandle2, L"Value4");
330 AssertValue(iniHandle2, L"Section1\\Value1", L"Section1Value1");
331 AssertValue(iniHandle2, L"Section1\\Value2", L"Section1Value2");
332 AssertValue(iniHandle2, L"Section2\\Value1", L"Section2Value1");
333 AssertValue(iniHandle2, L"Section2\\Array[0]", L"Arr");
334 }
335 finally
336 {
337 ReleaseIni(iniHandle);
338 ReleaseIni(iniHandle2);
339 }
340 }
341 };
342}
diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp
new file mode 100644
index 00000000..6dec9682
--- /dev/null
+++ b/src/test/DUtilUnitTest/MemUtilTest.cpp
@@ -0,0 +1,491 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 struct ArrayValue
12 {
13 DWORD dwNum;
14 void *pvNull1;
15 LPWSTR sczString;
16 void *pvNull2;
17 };
18
19 public ref class MemUtil
20 {
21 public:
22 [Fact]
23 void MemUtilAppendTest()
24 {
25 HRESULT hr = S_OK;
26 DWORD dwSize;
27 ArrayValue *rgValues = NULL;
28 DWORD cValues = 0;
29
30 try
31 {
32 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
33 NativeAssert::Succeeded(hr, "Failed to grow array size to 1");
34 ++cValues;
35 SetItem(rgValues + 0, 0);
36
37 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
38 NativeAssert::Succeeded(hr, "Failed to grow array size to 2");
39 ++cValues;
40 SetItem(rgValues + 1, 1);
41
42 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
43 NativeAssert::Succeeded(hr, "Failed to grow array size to 3");
44 ++cValues;
45 SetItem(rgValues + 2, 2);
46
47 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
48 NativeAssert::Succeeded(hr, "Failed to grow array size to 4");
49 ++cValues;
50 SetItem(rgValues + 3, 3);
51
52 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
53 NativeAssert::Succeeded(hr, "Failed to grow array size to 5");
54 ++cValues;
55 SetItem(rgValues + 4, 4);
56
57 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
58 NativeAssert::Succeeded(hr, "Failed to grow array size to 6");
59 ++cValues;
60 SetItem(rgValues + 5, 5);
61
62 // OK, we used growth size 5, so let's try ensuring we have space for 6 (5 + first item) items
63 // and make sure it doesn't grow since we already have enough space
64 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues, sizeof(ArrayValue), 5);
65 NativeAssert::Succeeded(hr, "Failed to ensure array size matches what it should already be");
66 dwSize = MemSize(rgValues);
67 if (dwSize != 6 * sizeof(ArrayValue))
68 {
69 hr = E_FAIL;
70 ExitOnFailure(hr, "MemEnsureArraySize is growing an array that is already big enough!");
71 }
72
73 for (DWORD i = 0; i < cValues; ++i)
74 {
75 CheckItem(rgValues + i, i);
76 }
77
78 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
79 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
80 ++cValues;
81 SetItem(rgValues + 6, 6);
82
83 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
84 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
85 ++cValues;
86 SetItem(rgValues + 7, 7);
87
88 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
89 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
90 ++cValues;
91 SetItem(rgValues + 8, 8);
92
93 for (DWORD i = 0; i < cValues; ++i)
94 {
95 CheckItem(rgValues + i, i);
96 }
97 }
98 finally
99 {
100 ReleaseMem(rgValues);
101 }
102
103 LExit:
104 return;
105 }
106
107 [Fact]
108 void MemUtilInsertTest()
109 {
110 HRESULT hr = S_OK;
111 ArrayValue *rgValues = NULL;
112 DWORD cValues = 0;
113
114 try
115 {
116 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
117 NativeAssert::Succeeded(hr, "Failed to insert into beginning of empty array");
118 ++cValues;
119 CheckNullItem(rgValues + 0);
120 SetItem(rgValues + 0, 5);
121
122 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5);
123 NativeAssert::Succeeded(hr, "Failed to insert at end of array");
124 ++cValues;
125 CheckNullItem(rgValues + 1);
126 SetItem(rgValues + 1, 6);
127
128 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
129 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
130 ++cValues;
131 CheckNullItem(rgValues + 0);
132 SetItem(rgValues + 0, 4);
133
134 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
135 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
136 ++cValues;
137 CheckNullItem(rgValues + 0);
138 SetItem(rgValues + 0, 3);
139
140 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
141 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
142 ++cValues;
143 CheckNullItem(rgValues + 0);
144 SetItem(rgValues + 0, 1);
145
146 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 1, 1, cValues + 1, sizeof(ArrayValue), 5);
147 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
148 ++cValues;
149 CheckNullItem(rgValues + 1);
150 SetItem(rgValues + 1, 2);
151
152 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 0, 1, cValues + 1, sizeof(ArrayValue), 5);
153 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
154 ++cValues;
155 CheckNullItem(rgValues + 0);
156 SetItem(rgValues + 0, 0);
157
158 for (DWORD i = 0; i < cValues; ++i)
159 {
160 CheckItem(rgValues + i, i);
161 }
162
163 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), cValues + 1, sizeof(ArrayValue), 5);
164 NativeAssert::Succeeded(hr, "Failed to grow array size to 7");
165 ++cValues;
166 CheckNullItem(rgValues + 7);
167 SetItem(rgValues + 7, 7);
168
169 hr = MemInsertIntoArray(reinterpret_cast<LPVOID*>(&rgValues), 8, 1, cValues + 1, sizeof(ArrayValue), 5);
170 NativeAssert::Succeeded(hr, "Failed to insert into beginning of array");
171 ++cValues;
172 CheckNullItem(rgValues + 8);
173 SetItem(rgValues + 8, 8);
174
175 for (DWORD i = 0; i < cValues; ++i)
176 {
177 CheckItem(rgValues + i, i);
178 }
179 }
180 finally
181 {
182 ReleaseMem(rgValues);
183 }
184 }
185
186 [Fact]
187 void MemUtilRemovePreserveOrderTest()
188 {
189 HRESULT hr = S_OK;
190 ArrayValue *rgValues = NULL;
191 DWORD cValues = 0;
192
193 try
194 {
195 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
196 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
197
198 cValues = 10;
199 for (DWORD i = 0; i < cValues; ++i)
200 {
201 SetItem(rgValues + i, i);
202 }
203
204 // Remove last item
205 MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), TRUE);
206 --cValues;
207
208 for (DWORD i = 0; i < cValues; ++i)
209 {
210 CheckItem(rgValues + i, i);
211 }
212
213 // Remove last two items
214 MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), TRUE);
215 cValues -= 2;
216
217 for (DWORD i = 0; i < cValues; ++i)
218 {
219 CheckItem(rgValues + i, i);
220 }
221
222 // Remove first item
223 MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), TRUE);
224 --cValues;
225
226 for (DWORD i = 0; i < cValues; ++i)
227 {
228 CheckItem(rgValues + i, i + 1);
229 }
230
231
232 // Remove first two items
233 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE);
234 cValues -= 2;
235
236 for (DWORD i = 0; i < cValues; ++i)
237 {
238 CheckItem(rgValues + i, i + 3);
239 }
240
241 // Remove middle two items
242 MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), TRUE);
243 cValues -= 2;
244
245 CheckItem(rgValues, 3);
246 CheckItem(rgValues + 1, 6);
247
248 // Remove last 2 items to ensure we don't crash
249 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), TRUE);
250 cValues -= 2;
251 }
252 finally
253 {
254 ReleaseMem(rgValues);
255 }
256 }
257
258 [Fact]
259 void MemUtilRemoveFastTest()
260 {
261 HRESULT hr = S_OK;
262 ArrayValue *rgValues = NULL;
263 DWORD cValues = 0;
264
265 try
266 {
267 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
268 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
269
270 cValues = 10;
271 for (DWORD i = 0; i < cValues; ++i)
272 {
273 SetItem(rgValues + i, i);
274 }
275
276 // Remove last item
277 MemRemoveFromArray(rgValues, 9, 1, cValues, sizeof(ArrayValue), FALSE);
278 --cValues;
279
280 for (DWORD i = 0; i < cValues; ++i)
281 {
282 CheckItem(rgValues + i, i);
283 }
284
285 // Remove last two items
286 MemRemoveFromArray(rgValues, 7, 2, cValues, sizeof(ArrayValue), FALSE);
287 cValues -= 2;
288
289 for (DWORD i = 0; i < cValues; ++i)
290 {
291 CheckItem(rgValues + i, i);
292 }
293
294 // Remove first item
295 MemRemoveFromArray(rgValues, 0, 1, cValues, sizeof(ArrayValue), FALSE);
296 --cValues;
297
298 CheckItem(rgValues, 6);
299 CheckItem(rgValues + 1, 1);
300 CheckItem(rgValues + 2, 2);
301 CheckItem(rgValues + 3, 3);
302 CheckItem(rgValues + 4, 4);
303 CheckItem(rgValues + 5, 5);
304
305 // Remove first two items
306 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE);
307 cValues -= 2;
308
309 CheckItem(rgValues, 4);
310 CheckItem(rgValues + 1, 5);
311 CheckItem(rgValues + 2, 2);
312 CheckItem(rgValues + 3, 3);
313
314
315 // Remove middle two items
316 MemRemoveFromArray(rgValues, 1, 2, cValues, sizeof(ArrayValue), FALSE);
317 cValues -= 2;
318
319 CheckItem(rgValues, 4);
320 CheckItem(rgValues + 1, 3);
321
322 // Remove last 2 items to ensure we don't crash
323 MemRemoveFromArray(rgValues, 0, 2, cValues, sizeof(ArrayValue), FALSE);
324 cValues -= 2;
325 }
326 finally
327 {
328 ReleaseMem(rgValues);
329 }
330 }
331
332 [Fact]
333 void MemUtilSwapTest()
334 {
335 HRESULT hr = S_OK;
336 ArrayValue *rgValues = NULL;
337 DWORD cValues = 0;
338
339 try
340 {
341 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&rgValues), 10, sizeof(ArrayValue), 10);
342 NativeAssert::Succeeded(hr, "Failed to grow array size to 10");
343
344 cValues = 10;
345 for (DWORD i = 0; i < cValues; ++i)
346 {
347 SetItem(rgValues + i, i);
348 }
349
350 // Swap first two
351 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
352 --cValues;
353
354 CheckItem(rgValues, 1);
355 CheckItem(rgValues + 1, 0);
356 for (DWORD i = 2; i < cValues; ++i)
357 {
358 CheckItem(rgValues + i, i);
359 }
360
361 // Swap them back
362 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
363 --cValues;
364
365 for (DWORD i = 0; i < cValues; ++i)
366 {
367 CheckItem(rgValues + i, i);
368 }
369
370 // Swap first and last items (index 0 and 9)
371 MemArraySwapItems(rgValues, 0, 9, sizeof(ArrayValue));
372 --cValues;
373
374 CheckItem(rgValues, 9);
375 CheckItem(rgValues + 9, 0);
376 for (DWORD i = 1; i < cValues - 1; ++i)
377 {
378 CheckItem(rgValues + i, i);
379 }
380
381 // Swap index 1 and 8
382 MemArraySwapItems(rgValues, 1, 8, sizeof(ArrayValue));
383 --cValues;
384
385 CheckItem(rgValues, 9);
386 CheckItem(rgValues + 1, 8);
387 CheckItem(rgValues + 8, 1);
388 CheckItem(rgValues + 9, 0);
389 for (DWORD i = 2; i < cValues - 2; ++i)
390 {
391 CheckItem(rgValues + i, i);
392 }
393
394 // Swap index 2 and 7
395 MemArraySwapItems(rgValues, 2, 7, sizeof(ArrayValue));
396 --cValues;
397
398 CheckItem(rgValues, 9);
399 CheckItem(rgValues + 1, 8);
400 CheckItem(rgValues + 2, 7);
401 CheckItem(rgValues + 7, 2);
402 CheckItem(rgValues + 8, 1);
403 CheckItem(rgValues + 9, 0);
404 for (DWORD i = 3; i < cValues - 3; ++i)
405 {
406 CheckItem(rgValues + i, i);
407 }
408
409 // Swap index 0 and 1
410 MemArraySwapItems(rgValues, 0, 1, sizeof(ArrayValue));
411 --cValues;
412
413 CheckItem(rgValues, 8);
414 CheckItem(rgValues + 1, 9);
415 CheckItem(rgValues + 2, 7);
416 CheckItem(rgValues + 7, 2);
417 CheckItem(rgValues + 8, 1);
418 CheckItem(rgValues + 9, 0);
419 for (DWORD i = 3; i < cValues - 3; ++i)
420 {
421 CheckItem(rgValues + i, i);
422 }
423 }
424 finally
425 {
426 ReleaseMem(rgValues);
427 }
428 }
429
430 private:
431 void SetItem(ArrayValue *pValue, DWORD dwValue)
432 {
433 HRESULT hr = S_OK;
434 pValue->dwNum = dwValue;
435
436 hr = StrAllocFormatted(&pValue->sczString, L"%u", dwValue);
437 NativeAssert::Succeeded(hr, "Failed to allocate string");
438 }
439
440 void CheckItem(ArrayValue *pValue, DWORD dwValue)
441 {
442 HRESULT hr = S_OK;
443 LPWSTR sczTemp = NULL;
444
445 try
446 {
447 NativeAssert::Equal(dwValue, pValue->dwNum);
448
449 hr = StrAllocFormatted(&sczTemp, L"%u", dwValue);
450 NativeAssert::Succeeded(hr, "Failed to allocate temp string");
451
452 NativeAssert::StringEqual(sczTemp, pValue->sczString, TRUE);
453
454 if (pValue->pvNull1 || pValue->pvNull2)
455 {
456 hr = E_FAIL;
457 ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!");
458 }
459 }
460 finally
461 {
462 ReleaseStr(sczTemp);
463 }
464
465 LExit:
466 return;
467 }
468
469 void CheckNullItem(ArrayValue *pValue)
470 {
471 HRESULT hr = S_OK;
472
473 NativeAssert::Equal<DWORD>(0, pValue->dwNum);
474
475 if (pValue->sczString)
476 {
477 hr = E_FAIL;
478 ExitOnFailure(hr, "Item found isn't NULL!");
479 }
480
481 if (pValue->pvNull1 || pValue->pvNull2)
482 {
483 hr = E_FAIL;
484 ExitOnFailure(hr, "One of the expected NULL values wasn't NULL!");
485 }
486
487 LExit:
488 return;
489 }
490 };
491}
diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp
new file mode 100644
index 00000000..a6ed32f1
--- /dev/null
+++ b/src/test/DUtilUnitTest/MonUtilTest.cpp
@@ -0,0 +1,487 @@
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 RemoveDirectory
5
6using namespace System;
7using namespace System::Collections::Generic;
8using namespace System::Runtime::InteropServices;
9using namespace Xunit;
10using namespace WixTest;
11
12namespace DutilTests
13{
14 const int PREWAIT = 20;
15 const int POSTWAIT = 480;
16 const int FULLWAIT = 500;
17 const int SILENCEPERIOD = 100;
18
19 struct RegKey
20 {
21 HRESULT hr;
22 HKEY hkRoot;
23 LPCWSTR wzSubKey;
24 REG_KEY_BITNESS kbKeyBitness;
25 BOOL fRecursive;
26 };
27 struct Directory
28 {
29 HRESULT hr;
30 LPCWSTR wzPath;
31 BOOL fRecursive;
32 };
33 struct Results
34 {
35 RegKey *rgRegKeys;
36 DWORD cRegKeys;
37 Directory *rgDirectories;
38 DWORD cDirectories;
39 };
40
41 public delegate void MonGeneralDelegate(HRESULT, LPVOID);
42
43 public delegate void MonDriveStatusDelegate(WCHAR, BOOL, LPVOID);
44
45 public delegate void MonDirectoryDelegate(HRESULT, LPCWSTR, BOOL, LPVOID, LPVOID);
46
47 public delegate void MonRegKeyDelegate(HRESULT, HKEY, LPCWSTR, REG_KEY_BITNESS, BOOL, LPVOID, LPVOID);
48
49 static void MonGeneral(
50 __in HRESULT /*hrResult*/,
51 __in_opt LPVOID /*pvContext*/
52 )
53 {
54 Assert::True(false);
55 }
56
57 static void MonDriveStatus(
58 __in WCHAR /*chDrive*/,
59 __in BOOL /*fArriving*/,
60 __in_opt LPVOID /*pvContext*/
61 )
62 {
63 }
64
65 static void MonDirectory(
66 __in HRESULT hrResult,
67 __in_z LPCWSTR wzPath,
68 __in_z BOOL fRecursive,
69 __in_opt LPVOID pvContext,
70 __in_opt LPVOID pvDirectoryContext
71 )
72 {
73 Assert::Equal(S_OK, hrResult);
74 Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvDirectoryContext));
75
76 HRESULT hr = S_OK;
77 Results *pResults = reinterpret_cast<Results *>(pvContext);
78
79 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgDirectories), pResults->cDirectories + 1, sizeof(Directory), 5);
80 NativeAssert::ValidReturnCode(hr, S_OK);
81 ++pResults->cDirectories;
82
83 pResults->rgDirectories[pResults->cDirectories - 1].hr = hrResult;
84 pResults->rgDirectories[pResults->cDirectories - 1].wzPath = wzPath;
85 pResults->rgDirectories[pResults->cDirectories - 1].fRecursive = fRecursive;
86 }
87
88 static void MonRegKey(
89 __in HRESULT hrResult,
90 __in HKEY hkRoot,
91 __in_z LPCWSTR wzSubKey,
92 __in REG_KEY_BITNESS kbKeyBitness,
93 __in_z BOOL fRecursive,
94 __in_opt LPVOID pvContext,
95 __in_opt LPVOID pvRegKeyContext
96 )
97 {
98 Assert::Equal<HRESULT>(S_OK, hrResult);
99 Assert::Equal<DWORD_PTR>(0, reinterpret_cast<DWORD_PTR>(pvRegKeyContext));
100
101 HRESULT hr = S_OK;
102 Results *pResults = reinterpret_cast<Results *>(pvContext);
103
104 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pResults->rgRegKeys), pResults->cRegKeys + 1, sizeof(RegKey), 5);
105 NativeAssert::ValidReturnCode(hr, S_OK);
106 ++pResults->cRegKeys;
107
108 pResults->rgRegKeys[pResults->cRegKeys - 1].hr = hrResult;
109 pResults->rgRegKeys[pResults->cRegKeys - 1].hkRoot = hkRoot;
110 pResults->rgRegKeys[pResults->cRegKeys - 1].wzSubKey = wzSubKey;
111 pResults->rgRegKeys[pResults->cRegKeys - 1].kbKeyBitness = kbKeyBitness;
112 pResults->rgRegKeys[pResults->cRegKeys - 1].fRecursive = fRecursive;
113 }
114
115 public ref class MonUtil
116 {
117 public:
118 void ClearResults(Results *pResults)
119 {
120 ReleaseNullMem(pResults->rgDirectories);
121 pResults->cDirectories = 0;
122 ReleaseNullMem(pResults->rgRegKeys);
123 pResults->cRegKeys = 0;
124 }
125
126 void RemoveDirectory(LPCWSTR wzPath)
127 {
128 DWORD dwRetryCount = 0;
129 const DWORD c_dwMaxRetryCount = 100;
130 const DWORD c_dwRetryInterval = 50;
131
132 HRESULT hr = DirEnsureDelete(wzPath, TRUE, TRUE);
133
134 // Monitoring a directory opens a handle to that directory, which means delete requests for that directory will succeed
135 // (and deletion will be "pending" until our monitor handle is closed)
136 // but deletion of the directory containing that directory cannot complete until the handle is closed. This means DirEnsureDelete()
137 // can sometimes encounter HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) failures, which just means it needs to retry a bit later
138 // (after the waiter thread wakes up, it will release the handle)
139 while (HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY) == hr && c_dwMaxRetryCount > dwRetryCount)
140 {
141 ::Sleep(c_dwRetryInterval);
142 ++dwRetryCount;
143 hr = DirEnsureDelete(wzPath, TRUE, TRUE);
144 }
145
146 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
147 }
148
149 void TestDirectory(MON_HANDLE handle, Results *pResults)
150 {
151 HRESULT hr = S_OK;
152 LPWSTR sczShallowPath = NULL;
153 LPWSTR sczParentPath = NULL;
154 LPWSTR sczDeepPath = NULL;
155 LPWSTR sczChildPath = NULL;
156 LPWSTR sczChildFilePath = NULL;
157
158 try
159 {
160 hr = PathExpand(&sczShallowPath, L"%TEMP%\\MonUtilTest\\", PATH_EXPAND_ENVIRONMENT);
161 NativeAssert::ValidReturnCode(hr, S_OK);
162
163 hr = PathExpand(&sczParentPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\", PATH_EXPAND_ENVIRONMENT);
164 NativeAssert::ValidReturnCode(hr, S_OK);
165
166 hr = PathExpand(&sczDeepPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\", PATH_EXPAND_ENVIRONMENT);
167 NativeAssert::ValidReturnCode(hr, S_OK);
168
169 hr = PathExpand(&sczChildPath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\", PATH_EXPAND_ENVIRONMENT);
170 NativeAssert::ValidReturnCode(hr, S_OK);
171
172 hr = PathExpand(&sczChildFilePath, L"%TEMP%\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\file.txt", PATH_EXPAND_ENVIRONMENT);
173 NativeAssert::ValidReturnCode(hr, S_OK);
174
175 RemoveDirectory(sczShallowPath);
176
177 hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL);
178 NativeAssert::ValidReturnCode(hr, S_OK);
179
180 hr = DirEnsureExists(sczParentPath, NULL);
181 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
182 // Make sure creating the parent directory does nothing, even after silence period
183 ::Sleep(FULLWAIT);
184 Assert::Equal<DWORD>(0, pResults->cDirectories);
185
186 // Now create the target path, no notification until after the silence period
187 hr = DirEnsureExists(sczDeepPath, NULL);
188 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
189 ::Sleep(PREWAIT);
190 Assert::Equal<DWORD>(0, pResults->cDirectories);
191
192 // Now after the full silence period, it should have triggered
193 ::Sleep(POSTWAIT);
194 Assert::Equal<DWORD>(1, pResults->cDirectories);
195 NativeAssert::ValidReturnCode(pResults->rgDirectories[0].hr, S_OK);
196
197 // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists.
198 RemoveDirectory(sczShallowPath);
199
200 ::Sleep(FULLWAIT);
201 Assert::Equal<DWORD>(2, pResults->cDirectories);
202 NativeAssert::ValidReturnCode(pResults->rgDirectories[1].hr, S_OK);
203
204 // Create the parent directory again, still should be nothing even after full silence period
205 hr = DirEnsureExists(sczParentPath, NULL);
206 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
207 ::Sleep(FULLWAIT);
208 Assert::Equal<DWORD>(2, pResults->cDirectories);
209
210 hr = DirEnsureExists(sczChildPath, NULL);
211 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
212 ::Sleep(PREWAIT);
213 Assert::Equal<DWORD>(2, pResults->cDirectories);
214
215 ::Sleep(POSTWAIT);
216 Assert::Equal<DWORD>(3, pResults->cDirectories);
217 NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK);
218
219 // Write a file to a deep child subfolder, and make sure it's detected
220 hr = FileFromString(sczChildFilePath, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM);
221 NativeAssert::ValidReturnCode(hr, S_OK);
222 ::Sleep(PREWAIT);
223 Assert::Equal<DWORD>(3, pResults->cDirectories);
224
225 ::Sleep(POSTWAIT);
226 Assert::Equal<DWORD>(4, pResults->cDirectories);
227 NativeAssert::ValidReturnCode(pResults->rgDirectories[2].hr, S_OK);
228
229 RemoveDirectory(sczParentPath);
230
231 ::Sleep(FULLWAIT);
232 Assert::Equal<DWORD>(5, pResults->cDirectories);
233 NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK);
234
235 // Now remove the directory from the list of things to monitor, and confirm changes are no longer tracked
236 hr = MonRemoveDirectory(handle, sczDeepPath, TRUE);
237 NativeAssert::ValidReturnCode(hr, S_OK);
238 ::Sleep(PREWAIT);
239
240 hr = DirEnsureExists(sczDeepPath, NULL);
241 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
242 ::Sleep(FULLWAIT);
243 Assert::Equal<DWORD>(5, pResults->cDirectories);
244 NativeAssert::ValidReturnCode(pResults->rgDirectories[3].hr, S_OK);
245
246 // Finally, add it back so we can test multiple things to monitor at once
247 hr = MonAddDirectory(handle, sczDeepPath, TRUE, SILENCEPERIOD, NULL);
248 NativeAssert::ValidReturnCode(hr, S_OK);
249 }
250 finally
251 {
252 ReleaseStr(sczShallowPath);
253 ReleaseStr(sczDeepPath);
254 ReleaseStr(sczParentPath);
255 }
256 }
257
258 void TestRegKey(MON_HANDLE handle, Results *pResults)
259 {
260 HRESULT hr = S_OK;
261 LPCWSTR wzShallowRegKey = L"Software\\MonUtilTest\\";
262 LPCWSTR wzParentRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\";
263 LPCWSTR wzDeepRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\";
264 LPCWSTR wzChildRegKey = L"Software\\MonUtilTest\\sub\\folder\\that\\might\\not\\exist\\some\\sub\\folder\\";
265 HKEY hk = NULL;
266
267 try
268 {
269 hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE);
270 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
271
272 hr = MonAddRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE, SILENCEPERIOD, NULL);
273 NativeAssert::ValidReturnCode(hr, S_OK);
274
275 hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
276 ReleaseRegKey(hk);
277 // Make sure creating the parent key does nothing, even after silence period
278 ::Sleep(FULLWAIT);
279 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
280 Assert::Equal<DWORD>(0, pResults->cRegKeys);
281
282 // Now create the target path, no notification until after the silence period
283 hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
284 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
285 ReleaseRegKey(hk);
286 ::Sleep(PREWAIT);
287 Assert::Equal<DWORD>(0, pResults->cRegKeys);
288
289 // Now after the full silence period, it should have triggered
290 ::Sleep(POSTWAIT);
291 Assert::Equal<DWORD>(1, pResults->cRegKeys);
292 NativeAssert::ValidReturnCode(pResults->rgRegKeys[0].hr, S_OK);
293
294 // Now delete the directory, along with a ton of parents. This verifies MonUtil will keep watching the closest parent that still exists.
295 hr = RegDelete(HKEY_CURRENT_USER, wzShallowRegKey, REG_KEY_32BIT, TRUE);
296 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE, E_PATHNOTFOUND);
297 ::Sleep(PREWAIT);
298 Assert::Equal<DWORD>(1, pResults->cRegKeys);
299
300 ::Sleep(FULLWAIT);
301 Assert::Equal<DWORD>(2, pResults->cRegKeys);
302 NativeAssert::ValidReturnCode(pResults->rgRegKeys[1].hr, S_OK);
303
304 // Create the parent directory again, still should be nothing even after full silence period
305 hr = RegCreate(HKEY_CURRENT_USER, wzParentRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
306 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
307 ReleaseRegKey(hk);
308 ::Sleep(FULLWAIT);
309 Assert::Equal<DWORD>(2, pResults->cRegKeys);
310
311 hr = RegCreate(HKEY_CURRENT_USER, wzChildRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
312 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
313 ::Sleep(PREWAIT);
314 Assert::Equal<DWORD>(2, pResults->cRegKeys);
315
316 ::Sleep(FULLWAIT);
317 Assert::Equal<DWORD>(3, pResults->cRegKeys);
318 NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK);
319
320 // Write a registry value to some deep child subkey, and make sure it's detected
321 hr = RegWriteString(hk, L"valuename", L"testvalue");
322 NativeAssert::ValidReturnCode(hr, S_OK);
323 ReleaseRegKey(hk);
324 ::Sleep(PREWAIT);
325 Assert::Equal<DWORD>(3, pResults->cRegKeys);
326
327 ::Sleep(FULLWAIT);
328 Assert::Equal<DWORD>(4, pResults->cRegKeys);
329 NativeAssert::ValidReturnCode(pResults->rgRegKeys[2].hr, S_OK);
330
331 hr = RegDelete(HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_32BIT, TRUE);
332 NativeAssert::ValidReturnCode(hr, S_OK);
333
334 ::Sleep(FULLWAIT);
335 Assert::Equal<DWORD>(5, pResults->cRegKeys);
336
337 // Now remove the regkey from the list of things to monitor, and confirm changes are no longer tracked
338 hr = MonRemoveRegKey(handle, HKEY_CURRENT_USER, wzDeepRegKey, REG_KEY_DEFAULT, TRUE);
339 NativeAssert::ValidReturnCode(hr, S_OK);
340
341 hr = RegCreate(HKEY_CURRENT_USER, wzDeepRegKey, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hk);
342 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
343 ReleaseRegKey(hk);
344 ::Sleep(FULLWAIT);
345 Assert::Equal<DWORD>(5, pResults->cRegKeys);
346 }
347 finally
348 {
349 ReleaseRegKey(hk);
350 }
351 }
352
353 void TestMoreThan64(MON_HANDLE handle, Results *pResults)
354 {
355 HRESULT hr = S_OK;
356 LPWSTR sczBaseDir = NULL;
357 LPWSTR sczDir = NULL;
358 LPWSTR sczFile = NULL;
359
360 try
361 {
362 hr = PathExpand(&sczBaseDir, L"%TEMP%\\ScalabilityTest\\", PATH_EXPAND_ENVIRONMENT);
363 NativeAssert::ValidReturnCode(hr, S_OK);
364
365 for (DWORD i = 0; i < 200; ++i)
366 {
367 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
368 NativeAssert::ValidReturnCode(hr, S_OK);
369
370 hr = DirEnsureExists(sczDir, NULL);
371 NativeAssert::ValidReturnCode(hr, S_OK, S_FALSE);
372
373 hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL);
374 NativeAssert::ValidReturnCode(hr, S_OK);
375 }
376
377 hr = PathConcat(sczDir, L"file.txt", &sczFile);
378 NativeAssert::ValidReturnCode(hr, S_OK);
379
380 hr = FileFromString(sczFile, 0, L"contents", FILE_ENCODING_UTF16_WITH_BOM);
381 NativeAssert::ValidReturnCode(hr, S_OK);
382
383 ::Sleep(FULLWAIT);
384 Assert::Equal<DWORD>(1, pResults->cDirectories);
385
386 for (DWORD i = 0; i < 199; ++i)
387 {
388 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
389 NativeAssert::ValidReturnCode(hr, S_OK);
390
391 hr = MonRemoveDirectory(handle, sczDir, FALSE);
392 NativeAssert::ValidReturnCode(hr, S_OK);
393 }
394 ::Sleep(FULLWAIT);
395
396 hr = FileFromString(sczFile, 0, L"contents2", FILE_ENCODING_UTF16_WITH_BOM);
397 NativeAssert::ValidReturnCode(hr, S_OK);
398
399 ::Sleep(FULLWAIT);
400 Assert::Equal<DWORD>(2, pResults->cDirectories);
401
402 for (DWORD i = 0; i < 199; ++i)
403 {
404 hr = StrAllocFormatted(&sczDir, L"%ls%u\\", sczBaseDir, i);
405 NativeAssert::ValidReturnCode(hr, S_OK);
406
407 hr = MonAddDirectory(handle, sczDir, FALSE, SILENCEPERIOD, NULL);
408 NativeAssert::ValidReturnCode(hr, S_OK);
409 }
410 ::Sleep(FULLWAIT);
411
412 hr = FileFromString(sczFile, 0, L"contents3", FILE_ENCODING_UTF16_WITH_BOM);
413 NativeAssert::ValidReturnCode(hr, S_OK);
414
415 ::Sleep(FULLWAIT);
416 Assert::Equal<DWORD>(3, pResults->cDirectories);
417 }
418 finally
419 {
420 ReleaseStr(sczBaseDir);
421 ReleaseStr(sczDir);
422 ReleaseStr(sczFile);
423 }
424 }
425
426 [Fact]
427 void MonUtilTest()
428 {
429 HRESULT hr = S_OK;
430 MON_HANDLE handle = NULL;
431 List<GCHandle>^ gcHandles = gcnew List<GCHandle>();
432 Results *pResults = (Results *)MemAlloc(sizeof(Results), TRUE);
433 Assert::True(NULL != pResults);
434
435 try
436 {
437 // These ensure the function pointers we send point to this thread's appdomain, which helps with assembly binding when running tests within msbuild
438 MonGeneralDelegate^ fpMonGeneral = gcnew MonGeneralDelegate(MonGeneral);
439 GCHandle gchMonGeneral = GCHandle::Alloc(fpMonGeneral);
440 gcHandles->Add(gchMonGeneral);
441 IntPtr ipMonGeneral = Marshal::GetFunctionPointerForDelegate(fpMonGeneral);
442
443 MonDriveStatusDelegate^ fpMonDriveStatus = gcnew MonDriveStatusDelegate(MonDriveStatus);
444 GCHandle gchMonDriveStatus = GCHandle::Alloc(fpMonDriveStatus);
445 gcHandles->Add(gchMonDriveStatus);
446 IntPtr ipMonDriveStatus = Marshal::GetFunctionPointerForDelegate(fpMonDriveStatus);
447
448 MonDirectoryDelegate^ fpMonDirectory = gcnew MonDirectoryDelegate(MonDirectory);
449 GCHandle gchMonDirectory = GCHandle::Alloc(fpMonDirectory);
450 gcHandles->Add(gchMonDirectory);
451 IntPtr ipMonDirectory = Marshal::GetFunctionPointerForDelegate(fpMonDirectory);
452
453 MonRegKeyDelegate^ fpMonRegKey = gcnew MonRegKeyDelegate(MonRegKey);
454 GCHandle gchMonRegKey = GCHandle::Alloc(fpMonRegKey);
455 gcHandles->Add(gchMonRegKey);
456 IntPtr ipMonRegKey = Marshal::GetFunctionPointerForDelegate(fpMonRegKey);
457
458 // "Silence period" is 100 ms
459 hr = MonCreate(&handle, static_cast<PFN_MONGENERAL>(ipMonGeneral.ToPointer()), static_cast<PFN_MONDRIVESTATUS>(ipMonDriveStatus.ToPointer()), static_cast<PFN_MONDIRECTORY>(ipMonDirectory.ToPointer()), static_cast<PFN_MONREGKEY>(ipMonRegKey.ToPointer()), pResults);
460 NativeAssert::ValidReturnCode(hr, S_OK);
461
462 hr = RegInitialize();
463 NativeAssert::ValidReturnCode(hr, S_OK);
464
465 TestDirectory(handle, pResults);
466 ClearResults(pResults);
467 TestRegKey(handle, pResults);
468 ClearResults(pResults);
469 TestMoreThan64(handle, pResults);
470 ClearResults(pResults);
471 }
472 finally
473 {
474 ReleaseMon(handle);
475
476 for each (GCHandle gcHandle in gcHandles)
477 {
478 gcHandle.Free();
479 }
480
481 ReleaseMem(pResults->rgDirectories);
482 ReleaseMem(pResults->rgRegKeys);
483 ReleaseMem(pResults);
484 }
485 }
486 };
487}
diff --git a/src/test/DUtilUnitTest/NativeAssert.h b/src/test/DUtilUnitTest/NativeAssert.h
new file mode 100644
index 00000000..b10910c0
--- /dev/null
+++ b/src/test/DUtilUnitTest/NativeAssert.h
@@ -0,0 +1,83 @@
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 WixTest {
6
7 using namespace System;
8 using namespace System::Collections::Generic;
9 using namespace System::Linq;
10 using namespace Xunit;
11
12 public ref class NativeAssert : WixAssert
13 {
14 public:
15 static void NotNull(LPCWSTR wz)
16 {
17 if (!wz)
18 {
19 Assert::NotNull(nullptr);
20 }
21 }
22
23 // For some reason, naming these NotStringEqual methods "NotEqual" breaks Intellisense in files that call any overload of the NotEqual method.
24 static void NotStringEqual(LPCWSTR expected, LPCWSTR actual)
25 {
26 NativeAssert::NotStringEqual(expected, actual, FALSE);
27 }
28
29 static void NotStringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase)
30 {
31 IEqualityComparer<String^>^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture;
32 Assert::NotEqual(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer);
33 }
34
35 // For some reason, naming these StringEqual methods "Equal" breaks Intellisense in files that call any overload of the Equal method.
36 static void StringEqual(LPCWSTR expected, LPCWSTR actual)
37 {
38 NativeAssert::StringEqual(expected, actual, FALSE);
39 }
40
41 static void StringEqual(LPCWSTR expected, LPCWSTR actual, BOOL ignoreCase)
42 {
43 IEqualityComparer<String^>^ comparer = ignoreCase ? StringComparer::InvariantCultureIgnoreCase : StringComparer::InvariantCulture;
44 Assert::Equal(NativeAssert::LPWSTRToString(expected), NativeAssert::LPWSTRToString(actual), comparer);
45 }
46
47 static void Succeeded(HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array<LPCSTR>^ zArgs)
48 {
49 array<Object^>^ formatArgs = gcnew array<Object^, 1>(zArgs->Length + 1);
50 formatArgs[0] = NativeAssert::LPSTRToString(zArg);
51 for (int i = 0; i < zArgs->Length; ++i)
52 {
53 formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]);
54 }
55 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs);
56 }
57
58 static void Succeeded(HRESULT hr, LPCSTR zFormat, ... array<LPCWSTR>^ wzArgs)
59 {
60 array<Object^>^ formatArgs = gcnew array<Object^, 1>(wzArgs->Length);
61 for (int i = 0; i < wzArgs->Length; ++i)
62 {
63 formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]);
64 }
65 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs);
66 }
67
68 static void ValidReturnCode(HRESULT hr, ... array<HRESULT>^ validReturnCodes)
69 {
70 Assert::Contains(hr, (IEnumerable<HRESULT>^)validReturnCodes);
71 }
72
73 private:
74 static String^ LPSTRToString(LPCSTR z)
75 {
76 return z ? gcnew String(z) : nullptr;
77 }
78 static String^ LPWSTRToString(LPCWSTR wz)
79 {
80 return wz ? gcnew String(wz) : nullptr;
81 }
82 };
83}
diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp
new file mode 100644
index 00000000..13ec3be3
--- /dev/null
+++ b/src/test/DUtilUnitTest/PathUtilTest.cpp
@@ -0,0 +1,80 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 public ref class PathUtil
12 {
13 public:
14 [Fact]
15 void PathGetHierarchyArrayTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR *rgsczPaths = NULL;
19 UINT cPaths = 0;
20
21 try
22 {
23 hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\a.txt", &rgsczPaths, &cPaths);
24 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path");
25 Assert::Equal<DWORD>(5, cPaths);
26 NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]);
27 NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]);
28 NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]);
29 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]);
30 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\a.txt", rgsczPaths[4]);
31 ReleaseNullStrArray(rgsczPaths, cPaths);
32
33 hr = PathGetHierarchyArray(L"c:\\foo\\bar\\bas\\", &rgsczPaths, &cPaths);
34 NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path");
35 Assert::Equal<DWORD>(4, cPaths);
36 NativeAssert::StringEqual(L"c:\\", rgsczPaths[0]);
37 NativeAssert::StringEqual(L"c:\\foo\\", rgsczPaths[1]);
38 NativeAssert::StringEqual(L"c:\\foo\\bar\\", rgsczPaths[2]);
39 NativeAssert::StringEqual(L"c:\\foo\\bar\\bas\\", rgsczPaths[3]);
40 ReleaseNullStrArray(rgsczPaths, cPaths);
41
42 hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\file.txt", &rgsczPaths, &cPaths);
43 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path");
44 Assert::Equal<DWORD>(3, cPaths);
45 NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]);
46 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]);
47 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\file.txt", rgsczPaths[2]);
48 ReleaseNullStrArray(rgsczPaths, cPaths);
49
50 hr = PathGetHierarchyArray(L"\\\\server\\share\\subdir\\", &rgsczPaths, &cPaths);
51 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
52 Assert::Equal<DWORD>(2, cPaths);
53 NativeAssert::StringEqual(L"\\\\server\\share\\", rgsczPaths[0]);
54 NativeAssert::StringEqual(L"\\\\server\\share\\subdir\\", rgsczPaths[1]);
55 ReleaseNullStrArray(rgsczPaths, cPaths);
56
57 hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\ValueName", &rgsczPaths, &cPaths);
58 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
59 Assert::Equal<DWORD>(4, cPaths);
60 NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]);
61 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
62 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
63 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\ValueName", rgsczPaths[3]);
64 ReleaseNullStrArray(rgsczPaths, cPaths);
65
66 hr = PathGetHierarchyArray(L"Software\\Microsoft\\Windows\\", &rgsczPaths, &cPaths);
67 NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
68 Assert::Equal<DWORD>(3, cPaths);
69 NativeAssert::StringEqual(L"Software\\", rgsczPaths[0]);
70 NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
71 NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
72 ReleaseNullStrArray(rgsczPaths, cPaths);
73 }
74 finally
75 {
76 ReleaseStrArray(rgsczPaths, cPaths);
77 }
78 }
79 };
80}
diff --git a/src/test/DUtilUnitTest/SceUtilTest.cpp b/src/test/DUtilUnitTest/SceUtilTest.cpp
new file mode 100644
index 00000000..75b9222a
--- /dev/null
+++ b/src/test/DUtilUnitTest/SceUtilTest.cpp
@@ -0,0 +1,488 @@
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#include <sqlce_oledb.h>
6#include <sceutil.h>
7
8using namespace System;
9using namespace Xunit;
10using namespace WixTest;
11
12#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);};
13
14namespace DutilTests
15{
16 enum TABLES
17 {
18 TABLE_A,
19 TABLE_COUNT
20 };
21
22 enum TABLE_A_COLUMNS
23 {
24 TABLE_A_KEY,
25 TABLE_A_BINARY,
26 TABLE_A_DWORD,
27 TABLE_A_QWORD,
28 TABLE_A_BOOL,
29 TABLE_A_STRING,
30 TABLE_A_DWORD_NULLABLE,
31 TABLE_A_INITIAL_COLUMNS,
32
33 TABLE_A_EXTRA_STRING = TABLE_A_INITIAL_COLUMNS,
34 TABLE_A_FINAL_COLUMNS
35 };
36
37 struct TableARowValue
38 {
39 DWORD dwAutoGenKey;
40
41 BYTE *pbBinary;
42 DWORD cBinary;
43
44 DWORD dw;
45 DWORD64 qw;
46 BOOL f;
47 LPWSTR scz;
48
49 BOOL fNullablePresent;
50 DWORD dwNullable;
51
52 BOOL fSchemaV2;
53 LPWSTR sczExtra;
54 };
55
56 public ref class SceUtil
57 {
58 public:
59 void ReleaseSceSchema(SCE_DATABASE_SCHEMA *pdsSchema)
60 {
61 DWORD dwTable;
62
63 for (dwTable = 0; dwTable < pdsSchema->cTables; ++dwTable)
64 {
65 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgColumns);
66 ReleaseNullMem(pdsSchema->rgTables[dwTable].rgIndexes);
67 }
68
69 ReleaseMem(pdsSchema->rgTables);
70
71 return;
72 }
73
74 void SetupSchema(SCE_DATABASE_SCHEMA *pSchema, BOOL fIncludeExtended)
75 {
76 pSchema->cTables = TABLE_COUNT;
77 pSchema->rgTables = static_cast<SCE_TABLE_SCHEMA*>(MemAlloc(TABLE_COUNT * sizeof(SCE_TABLE_SCHEMA), TRUE));
78 NativeAssert::True(pSchema->rgTables != NULL);
79
80 pSchema->rgTables[TABLE_A].wzName = L"TableA";
81 pSchema->rgTables[TABLE_A].cColumns = fIncludeExtended ? TABLE_A_FINAL_COLUMNS : TABLE_A_INITIAL_COLUMNS;
82 pSchema->rgTables[TABLE_A].cIndexes = 2;
83
84 for (DWORD i = 0; i < pSchema->cTables; ++i)
85 {
86 pSchema->rgTables[i].rgColumns = static_cast<SCE_COLUMN_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cColumns, TRUE));
87 NativeAssert::True(pSchema->rgTables[i].rgColumns != NULL);
88
89 pSchema->rgTables[i].rgIndexes = static_cast<SCE_INDEX_SCHEMA*>(MemAlloc(sizeof(SCE_COLUMN_SCHEMA) * pSchema->rgTables[i].cIndexes, TRUE));
90 NativeAssert::True(pSchema->rgTables[i].rgIndexes != NULL);
91 }
92
93 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].wzName = L"Key";
94 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].dbtColumnType = DBTYPE_I4;
95 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fPrimaryKey = TRUE;
96 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_KEY].fAutoIncrement = TRUE;
97 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].wzName = L"Binary";
98 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BINARY].dbtColumnType = DBTYPE_BYTES;
99 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].wzName = L"Dword";
100 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD].dbtColumnType = DBTYPE_I4;
101 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].wzName = L"Qword";
102 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_QWORD].dbtColumnType = DBTYPE_I8;
103 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].wzName = L"Bool";
104 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_BOOL].dbtColumnType = DBTYPE_BOOL;
105 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].wzName = L"String";
106 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_STRING].dbtColumnType = DBTYPE_WSTR;
107 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].wzName = L"Nullable";
108 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].dbtColumnType = DBTYPE_I4;
109 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_DWORD_NULLABLE].fNullable = TRUE;
110
111 if (fIncludeExtended)
112 {
113 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].wzName = L"ExtraString";
114 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].dbtColumnType = DBTYPE_WSTR;
115 pSchema->rgTables[TABLE_A].rgColumns[TABLE_A_EXTRA_STRING].fNullable = TRUE;
116 }
117
118 static DWORD rgdwTableA_Index1[] = { TABLE_A_DWORD, TABLE_A_STRING, TABLE_A_QWORD };
119 static DWORD rgdwTableA_Index2[] = { TABLE_A_DWORD, TABLE_A_STRING };
120
121 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[0], rgdwTableA_Index1, L"Dword_String_Qword");
122 ASSIGN_INDEX_STRUCT(pSchema->rgTables[TABLE_A].rgIndexes[1], rgdwTableA_Index2, L"Dword_String");
123 }
124
125 void SetStructValues(TableARowValue *pValue, BYTE *pbBinary, DWORD cBinary, DWORD dw, DWORD64 qw, BOOL f, LPWSTR scz, DWORD *pdw, LPWSTR sczExtra)
126 {
127 pValue->pbBinary = pbBinary;
128 pValue->cBinary = cBinary;
129 pValue->dw = dw;
130 pValue->qw = qw;
131 pValue->f = f;
132 pValue->scz = scz;
133
134 if (pdw)
135 {
136 pValue->fNullablePresent = TRUE;
137 pValue->dwNullable = *pdw;
138 }
139 else
140 {
141 pValue->fNullablePresent = FALSE;
142 }
143
144 if (sczExtra)
145 {
146 pValue->fSchemaV2 = TRUE;
147 pValue->sczExtra = sczExtra;
148 }
149 else
150 {
151 pValue->fSchemaV2 = FALSE;
152 }
153 }
154
155 void AssertStructValuesSame(TableARowValue *pValueExpected, TableARowValue *pValueOther)
156 {
157 NativeAssert::Equal(pValueExpected->cBinary, pValueOther->cBinary);
158 NativeAssert::True(0 == memcmp(pValueExpected->pbBinary, pValueOther->pbBinary, pValueOther->cBinary));
159
160 NativeAssert::Equal(pValueExpected->dw, pValueOther->dw);
161 NativeAssert::Equal(pValueExpected->qw, pValueOther->qw);
162 NativeAssert::Equal(pValueExpected->f, pValueOther->f);
163 NativeAssert::True(0 == wcscmp(pValueExpected->scz, pValueOther->scz));
164
165 NativeAssert::Equal(pValueExpected->fNullablePresent, pValueOther->fNullablePresent);
166 if (pValueExpected->fNullablePresent)
167 {
168 NativeAssert::Equal(pValueExpected->dwNullable, pValueOther->dwNullable);
169 }
170
171 NativeAssert::Equal(pValueExpected->fSchemaV2, pValueOther->fSchemaV2);
172 if (pValueExpected->fSchemaV2)
173 {
174 NativeAssert::True(0 == wcscmp(pValueExpected->sczExtra, pValueOther->sczExtra));
175 }
176 }
177
178 void InsertRow(SCE_DATABASE *pDatabase, TableARowValue *pValue, BOOL fRollback)
179 {
180 HRESULT hr = S_OK;
181 SCE_ROW_HANDLE sceRow = NULL;
182
183 hr = SceBeginTransaction(pDatabase);
184 NativeAssert::Succeeded(hr, "Failed to begin transaction");
185
186 hr = ScePrepareInsert(pDatabase, TABLE_A, &sceRow);
187 NativeAssert::Succeeded(hr, "Failed to prepare to insert row");
188
189 hr = SceSetColumnBinary(sceRow, TABLE_A_BINARY, pValue->pbBinary, pValue->cBinary);
190 NativeAssert::Succeeded(hr, "Failed to set binary value");
191
192 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD, pValue->dw);
193 NativeAssert::Succeeded(hr, "Failed to set dword value");
194
195 hr = SceSetColumnQword(sceRow, TABLE_A_QWORD, pValue->qw);
196 NativeAssert::Succeeded(hr, "Failed to set qword value");
197
198 hr = SceSetColumnBool(sceRow, TABLE_A_BOOL, pValue->f);
199 NativeAssert::Succeeded(hr, "Failed to set bool value");
200
201 hr = SceSetColumnString(sceRow, TABLE_A_STRING, pValue->scz);
202 NativeAssert::Succeeded(hr, "Failed to set string value");
203
204 if (pValue->fNullablePresent)
205 {
206 hr = SceSetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, pValue->dwNullable);
207 NativeAssert::Succeeded(hr, "Failed to set dword value");
208 }
209 else
210 {
211 hr = SceSetColumnNull(sceRow, TABLE_A_DWORD_NULLABLE);
212 NativeAssert::Succeeded(hr, "Failed to set null value");
213 }
214
215 if (pValue->fSchemaV2)
216 {
217 hr = SceSetColumnString(sceRow, TABLE_A_EXTRA_STRING, pValue->sczExtra);
218 NativeAssert::Succeeded(hr, "Failed to set extra string value");
219 }
220
221 hr = SceFinishUpdate(sceRow);
222 NativeAssert::Succeeded(hr, "Failed to finish insert");
223
224 if (fRollback)
225 {
226 hr = SceRollbackTransaction(pDatabase);
227 NativeAssert::Succeeded(hr, "Failed to rollback transaction");
228 }
229 else
230 {
231 hr = SceCommitTransaction(pDatabase);
232 NativeAssert::Succeeded(hr, "Failed to commit transaction");
233
234 hr = SceGetColumnDword(sceRow, TABLE_A_KEY, &pValue->dwAutoGenKey);
235 NativeAssert::Succeeded(hr, "Failed to get autogen key after insert");
236
237 NativeAssert::True(pValue->dwAutoGenKey != 0);
238 }
239
240 ReleaseSceRow(sceRow);
241 }
242
243 void VerifyRow(TableARowValue *pExpectedValue, SCE_ROW_HANDLE sceRow)
244 {
245 HRESULT hr = S_OK;
246 TableARowValue value = {};
247
248 hr = SceGetColumnBinary(sceRow, TABLE_A_BINARY, &value.pbBinary, &value.cBinary);
249 NativeAssert::Succeeded(hr, "Failed to get binary value from result row");
250
251 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD, &value.dw);
252 NativeAssert::Succeeded(hr, "Failed to get dword value from result row");
253
254 hr = SceGetColumnQword(sceRow, TABLE_A_QWORD, &value.qw);
255 NativeAssert::Succeeded(hr, "Failed to get qword value from result row");
256
257 hr = SceGetColumnBool(sceRow, TABLE_A_BOOL, &value.f);
258 NativeAssert::Succeeded(hr, "Failed to get bool value from result row");
259
260 hr = SceGetColumnString(sceRow, TABLE_A_STRING, &value.scz);
261 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
262
263 hr = SceGetColumnDword(sceRow, TABLE_A_DWORD_NULLABLE, &value.dwNullable);
264 if (hr == E_NOTFOUND)
265 {
266 value.fNullablePresent = FALSE;
267 hr = S_OK;
268 }
269 else
270 {
271 NativeAssert::Succeeded(hr, "Failed to get string value from result row");
272 value.fNullablePresent = TRUE;
273 }
274
275 if (pExpectedValue->fSchemaV2)
276 {
277 value.fSchemaV2 = TRUE;
278 hr = SceGetColumnString(sceRow, TABLE_A_EXTRA_STRING, &value.sczExtra);
279 NativeAssert::Succeeded(hr, "Failed to get extra string value from result row");
280 }
281
282 AssertStructValuesSame(pExpectedValue, &value);
283
284 ReleaseNullMem(value.pbBinary);
285 ReleaseNullStr(value.scz);
286 }
287
288 void VerifyQuery(TableARowValue **rgExpectedValues, DWORD cExpectedValues, SCE_QUERY_RESULTS_HANDLE queryResults)
289 {
290 HRESULT hr = S_OK;
291 SCE_ROW_HANDLE sceRow = NULL;
292
293 for (DWORD i = 0; i < cExpectedValues; ++i)
294 {
295 hr = SceGetNextResultRow(queryResults, &sceRow);
296 NativeAssert::Succeeded(hr, "Failed to get next result row");
297
298 VerifyRow(rgExpectedValues[i], sceRow);
299 ReleaseNullSceRow(sceRow);
300 }
301
302 // No more results
303 NativeAssert::True(NULL == queryResults || FAILED(SceGetNextResultRow(queryResults, &sceRow)));
304 }
305
306 void TestIndex(SCE_DATABASE *pDatabase)
307 {
308 HRESULT hr = S_OK;
309 BYTE binary1[50] = { 0x80, 0x70 };
310 BYTE binary2[40] = { 0x90, 0xAB };
311 BYTE binary3[40] = { 0x85, 0x88 };
312 DWORD dwValue1 = 0x55555555, dwValue2 = 0x88888888;
313 TableARowValue value1 = {}, value2 = {}, value3 = {}, value4 = {}, value5 = {};
314 SCE_QUERY_HANDLE query = NULL;
315 SCE_QUERY_RESULTS_HANDLE results = NULL;
316
317 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 3, 1, TRUE, L"zzz", &dwValue1, NULL);
318 SetStructValues(&value2, static_cast<BYTE *>(binary2), sizeof(binary2), 3, 2, TRUE, L"yyy", &dwValue2, NULL);
319 SetStructValues(&value3, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 3, TRUE, L"xxx", NULL, NULL);
320 SetStructValues(&value4, static_cast<BYTE *>(binary2), sizeof(binary2), 4, 4, TRUE, L"xyz", &dwValue2, NULL);
321 SetStructValues(&value5, static_cast<BYTE *>(binary3), sizeof(binary3), 3, 1, TRUE, L"yyy", &dwValue2, NULL);
322
323 // Rollback an insert to confirm the insert doesn't happen and database can still be interacted with normally afterwards
324 InsertRow(pDatabase, &value1, TRUE);
325
326 InsertRow(pDatabase, &value1, FALSE);
327 InsertRow(pDatabase, &value2, FALSE);
328 InsertRow(pDatabase, &value3, FALSE);
329 InsertRow(pDatabase, &value4, FALSE);
330 InsertRow(pDatabase, &value5, FALSE);
331
332 NativeAssert::True(value1.dwAutoGenKey != value2.dwAutoGenKey);
333
334 // Test setting 1 column
335 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
336 NativeAssert::Succeeded(hr, "Failed to begin query");
337
338 hr = SceSetQueryColumnDword(query, 3);
339 NativeAssert::Succeeded(hr, "Failed to set query column dword");
340
341 hr = SceRunQueryRange(&query, &results);
342 NativeAssert::Succeeded(hr, "Failed to run query");
343 NativeAssert::True(query == NULL);
344
345 TableARowValue *sortedAfterQuery1[] = { &value3, &value5, &value2, &value1 };
346 VerifyQuery(sortedAfterQuery1, _countof(sortedAfterQuery1), results);
347 ReleaseNullSceQueryResults(results);
348
349 // Test setting 2 columns, third column is unspecified so results are sorted by it
350 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
351 NativeAssert::Succeeded(hr, "Failed to begin query");
352
353 hr = SceSetQueryColumnDword(query, 3);
354 NativeAssert::Succeeded(hr, "Failed to set query column dword");
355
356 hr = SceSetQueryColumnString(query, L"yyy");
357 NativeAssert::Succeeded(hr, "Failed to set query column dword");
358
359 hr = SceRunQueryRange(&query, &results);
360 NativeAssert::Succeeded(hr, "Failed to run query");
361 NativeAssert::True(query == NULL);
362
363 TableARowValue *sortedAfterQuery2[] = { &value5, &value2 };
364 VerifyQuery(sortedAfterQuery2, _countof(sortedAfterQuery2), results);
365 ReleaseNullSceQueryResults(results);
366
367 // Test setting 2 columns, third column of index is unspecified so results are sorted by it
368 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
369 NativeAssert::Succeeded(hr, "Failed to begin query");
370
371 hr = SceSetQueryColumnDword(query, 3);
372 NativeAssert::Succeeded(hr, "Failed to set query column dword");
373
374 hr = SceSetQueryColumnString(query, L"yyy");
375 NativeAssert::Succeeded(hr, "Failed to set query column dword");
376
377 hr = SceRunQueryRange(&query, &results);
378 NativeAssert::Succeeded(hr, "Failed to run query");
379 NativeAssert::True(query == NULL);
380
381 TableARowValue *sortedAfterQuery3[] = { &value5, &value2 };
382 VerifyQuery(sortedAfterQuery3, _countof(sortedAfterQuery3), results);
383 ReleaseNullSceQueryResults(results);
384
385 // Test setting 2 columns in a different (2 column) index, so there is no 3rd column in index to sort by
386 hr = SceBeginQuery(pDatabase, TABLE_A, 1, &query);
387 NativeAssert::Succeeded(hr, "Failed to begin query");
388
389 hr = SceSetQueryColumnDword(query, 3);
390 NativeAssert::Succeeded(hr, "Failed to set query column dword");
391
392 hr = SceSetQueryColumnString(query, L"yyy");
393 NativeAssert::Succeeded(hr, "Failed to set query column dword");
394
395 hr = SceRunQueryRange(&query, &results);
396 NativeAssert::Succeeded(hr, "Failed to run query");
397 NativeAssert::True(query == NULL);
398
399 TableARowValue *sortedAfterQuery4[] = { &value2, &value5 };
400 VerifyQuery(sortedAfterQuery4, _countof(sortedAfterQuery4), results);
401 ReleaseNullSceQueryResults(results);
402 }
403
404 void TestReadWriteSchemaV2(SCE_DATABASE *pDatabase)
405 {
406 HRESULT hr = S_OK;
407 BYTE binary1[40] = { 0x55, 0x44 };
408 DWORD dwValue1 = 58;
409 TableARowValue value1 = {};
410 SCE_QUERY_HANDLE query = NULL;
411 SCE_ROW_HANDLE row = NULL;
412
413 SetStructValues(&value1, static_cast<BYTE *>(binary1), sizeof(binary1), 5, 1, TRUE, L"zzz", &dwValue1, L"newextrastring");
414
415 InsertRow(pDatabase, &value1, FALSE);
416
417 // Test setting 1 column
418 hr = SceBeginQuery(pDatabase, TABLE_A, 0, &query);
419 NativeAssert::Succeeded(hr, "Failed to begin query");
420
421 hr = SceSetQueryColumnDword(query, 5);
422 NativeAssert::Succeeded(hr, "Failed to set query column dword");
423
424 hr = SceRunQueryExact(&query, &row);
425 NativeAssert::Succeeded(hr, "Failed to run query exact");
426
427 VerifyRow(&value1, row);
428 }
429
430 [Fact]
431 void SceUtilTest()
432 {
433 HRESULT hr = S_OK;
434 BOOL fComInitialized = FALSE;
435 LPWSTR sczDbPath = NULL;
436 SCE_DATABASE *pDatabase = NULL;
437 SCE_DATABASE_SCHEMA schema1 = {};
438 SCE_DATABASE_SCHEMA schema2 = {};
439
440 try
441 {
442 hr = ::CoInitialize(0);
443 NativeAssert::Succeeded(hr, "Failed to initialize COM");
444 fComInitialized = TRUE;
445
446 SetupSchema(&schema1, FALSE);
447 SetupSchema(&schema2, TRUE);
448
449 hr = PathExpand(&sczDbPath, L"%TEMP%\\SceUtilTest\\UnitTest.sdf", PATH_EXPAND_ENVIRONMENT);
450 NativeAssert::Succeeded(hr, "Failed to get path to test database");
451
452 FileEnsureDelete(sczDbPath);
453
454 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema1, &pDatabase);
455 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
456
457 TestIndex(pDatabase);
458
459 hr = SceCloseDatabase(pDatabase);
460 pDatabase = NULL;
461 NativeAssert::Succeeded(hr, "Failed to close database");
462
463 // Add column to schema
464 hr = SceEnsureDatabase(sczDbPath, L"sqlceoledb40.dll", L"Test", 1, &schema2, &pDatabase);
465 NativeAssert::Succeeded(hr, "Failed to ensure database schema");
466
467 TestReadWriteSchemaV2(pDatabase);
468 }
469 finally
470 {
471 ReleaseSceSchema(&schema1);
472 ReleaseSceSchema(&schema2);
473
474 if (NULL != pDatabase)
475 {
476 hr = SceCloseDatabase(pDatabase);
477 NativeAssert::Succeeded(hr, "Failed to close database");
478 }
479 ReleaseStr(sczDbPath);
480
481 if (fComInitialized)
482 {
483 ::CoUninitialize();
484 }
485 }
486 }
487 };
488}
diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp
new file mode 100644
index 00000000..406f2f23
--- /dev/null
+++ b/src/test/DUtilUnitTest/StrUtilTest.cpp
@@ -0,0 +1,184 @@
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;
6using namespace Xunit;
7using namespace WixTest;
8
9namespace DutilTests
10{
11 public ref class StrUtil
12 {
13 public:
14 [Fact]
15 void StrUtilFormattedTest()
16 {
17 HRESULT hr = S_OK;
18 LPWSTR sczText = NULL;
19
20 try
21 {
22 hr = StrAllocFormatted(&sczText, L"%hs - %ls - %u", "ansi string", L"unicode string", 1234);
23 NativeAssert::Succeeded(hr, "Failed to format string.");
24 NativeAssert::StringEqual(L"ansi string - unicode string - 1234", sczText);
25
26 ReleaseNullStr(sczText);
27
28 hr = StrAllocString(&sczText, L"repeat", 0);
29 NativeAssert::Succeeded(hr, "Failed to allocate string.");
30
31 hr = StrAllocFormatted(&sczText, L"%ls and %ls", sczText, sczText);
32 NativeAssert::Succeeded(hr, "Failed to format string unto itself.");
33 NativeAssert::StringEqual(L"repeat and repeat", sczText);
34 }
35 finally
36 {
37 ReleaseStr(sczText);
38 }
39 }
40
41 [Fact]
42 void StrUtilTrimTest()
43 {
44 TestTrim(L"", L"");
45 TestTrim(L"Blah", L"Blah");
46 TestTrim(L"\t\t\tBlah", L"Blah");
47 TestTrim(L"\t Blah ", L"Blah");
48 TestTrim(L"Blah ", L"Blah");
49 TestTrim(L"\t Spaces \t Between \t", L"Spaces \t Between");
50 TestTrim(L" \t\t\t ", L"");
51
52 TestTrimAnsi("", "");
53 TestTrimAnsi("Blah", "Blah");
54 TestTrimAnsi("\t\t\tBlah", "Blah");
55 TestTrimAnsi(" Blah ", "Blah");
56 TestTrimAnsi("Blah ", "Blah");
57 TestTrimAnsi("\t Spaces \t Between \t", "Spaces \t Between");
58 TestTrimAnsi(" \t\t\t ", "");
59 }
60
61 [Fact]
62 void StrUtilConvertTest()
63 {
64 char a[] = { 'a', 'b', 'C', 'd', '\0', '\0' };
65
66 TestStrAllocStringAnsi(a, 5, L"abCd");
67 TestStrAllocStringAnsi(a, 4, L"abCd");
68 TestStrAllocStringAnsi(a, 3, L"abC");
69 TestStrAllocStringAnsi(a, 2, L"ab");
70 TestStrAllocStringAnsi(a, 1, L"a");
71 TestStrAllocStringAnsi(a, 0, L"abCd");
72
73 wchar_t b[] = { L'a', L'b', L'C', L'd', L'\0', L'\0' };
74
75 TestStrAnsiAllocString(b, 5, "abCd");
76 TestStrAnsiAllocString(b, 4, "abCd");
77 TestStrAnsiAllocString(b, 3, "abC");
78 TestStrAnsiAllocString(b, 2, "ab");
79 TestStrAnsiAllocString(b, 1, "a");
80 TestStrAnsiAllocString(b, 0, "abCd");
81 }
82
83 private:
84 void TestTrim(LPCWSTR wzInput, LPCWSTR wzExpectedResult)
85 {
86 HRESULT hr = S_OK;
87 LPWSTR sczOutput = NULL;
88
89 try
90 {
91 hr = StrTrimWhitespace(&sczOutput, wzInput);
92 NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: {0}", wzInput);
93
94 if (0 != wcscmp(wzExpectedResult, sczOutput))
95 {
96 hr = E_FAIL;
97 ExitOnFailure(hr, "Trimmed string \"%ls\", expected result \"%ls\", actual result \"%ls\"", wzInput, wzExpectedResult, sczOutput);
98 }
99 }
100 finally
101 {
102 ReleaseStr(sczOutput);
103 }
104
105 LExit:
106 return;
107 }
108
109 void TestTrimAnsi(LPCSTR szInput, LPCSTR szExpectedResult)
110 {
111 HRESULT hr = S_OK;
112 LPSTR sczOutput = NULL;
113
114 try
115 {
116 hr = StrAnsiTrimWhitespace(&sczOutput, szInput);
117 NativeAssert::Succeeded(hr, "Failed to trim whitespace from string: \"{0}\"", szInput);
118
119 if (0 != strcmp(szExpectedResult, sczOutput))
120 {
121 hr = E_FAIL;
122 ExitOnFailure(hr, "Trimmed string \"%hs\", expected result \"%hs\", actual result \"%hs\"", szInput, szExpectedResult, sczOutput);
123 }
124 }
125 finally
126 {
127 ReleaseStr(sczOutput);
128 }
129
130 LExit:
131 return;
132 }
133
134 void TestStrAllocStringAnsi(LPCSTR szSource, DWORD cchSource, LPCWSTR wzExpectedResult)
135 {
136 HRESULT hr = S_OK;
137 LPWSTR sczOutput = NULL;
138
139 try
140 {
141 hr = StrAllocStringAnsi(&sczOutput, szSource, cchSource, CP_UTF8);
142 NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", szSource);
143
144 if (0 != wcscmp(sczOutput, wzExpectedResult))
145 {
146 hr = E_FAIL;
147 ExitOnFailure(hr, "String doesn't match, expected result \"%ls\", actual result \"%ls\"", wzExpectedResult, sczOutput);
148 }
149 }
150 finally
151 {
152 ReleaseStr(sczOutput);
153 }
154
155 LExit:
156 return;
157 }
158
159 void TestStrAnsiAllocString(LPWSTR wzSource, DWORD cchSource, LPCSTR szExpectedResult)
160 {
161 HRESULT hr = S_OK;
162 LPSTR sczOutput = NULL;
163
164 try
165 {
166 hr = StrAnsiAllocString(&sczOutput, wzSource, cchSource, CP_UTF8);
167 NativeAssert::Succeeded(hr, "Failed to call StrAllocStringAnsi on string: \"{0}\"", wzSource);
168
169 if (0 != strcmp(sczOutput, szExpectedResult))
170 {
171 hr = E_FAIL;
172 ExitOnFailure(hr, "String doesn't match, expected result \"%hs\", actual result \"%hs\"", szExpectedResult, sczOutput);
173 }
174 }
175 finally
176 {
177 ReleaseStr(sczOutput);
178 }
179
180 LExit:
181 return;
182 }
183 };
184}
diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc
new file mode 100644
index 00000000..bf68360a
--- /dev/null
+++ b/src/test/DUtilUnitTest/UnitTest.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 "UnitTest.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/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp
new file mode 100644
index 00000000..220b3ff5
--- /dev/null
+++ b/src/test/DUtilUnitTest/UriUtilTest.cpp
@@ -0,0 +1,96 @@
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;
6using namespace System::Text;
7using namespace System::Collections::Generic;
8using namespace Xunit;
9
10namespace CfgTests
11{
12 public ref class UriUtil
13 {
14 public:
15 [Fact]
16 void UriProtocolTest()
17 {
18 HRESULT hr = S_OK;
19
20 LPCWSTR uri = L"https://localhost/";
21 URI_PROTOCOL uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
22 hr = UriProtocol(uri, &uriProtocol);
23 ExitOnFailure(hr, "Failed to determine UriProtocol");
24 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
25
26 uri = L"HTTPS://localhost/";
27 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
28 hr = UriProtocol(uri, &uriProtocol);
29 ExitOnFailure(hr, "Failed to determine UriProtocol");
30 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
31
32 uri = L"HtTpS://localhost/";
33 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
34 hr = UriProtocol(uri, &uriProtocol);
35 ExitOnFailure(hr, "Failed to determine UriProtocol");
36 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTPS, (int)uriProtocol);
37
38 uri = L"HTTP://localhost/";
39 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
40 hr = UriProtocol(uri, &uriProtocol);
41 ExitOnFailure(hr, "Failed to determine UriProtocol");
42 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
43
44 uri = L"http://localhost/";
45 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
46 hr = UriProtocol(uri, &uriProtocol);
47 ExitOnFailure(hr, "Failed to determine UriProtocol");
48 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
49
50 uri = L"HtTp://localhost/";
51 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
52 hr = UriProtocol(uri, &uriProtocol);
53 ExitOnFailure(hr, "Failed to determine UriProtocol");
54 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_HTTP, (int)uriProtocol);
55
56 uri = L"file://localhost/";
57 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
58 hr = UriProtocol(uri, &uriProtocol);
59 ExitOnFailure(hr, "Failed to determine UriProtocol");
60 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
61
62 uri = L"FILE://localhost/";
63 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
64 hr = UriProtocol(uri, &uriProtocol);
65 ExitOnFailure(hr, "Failed to determine UriProtocol");
66 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
67
68 uri = L"FiLe://localhost/";
69 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
70 hr = UriProtocol(uri, &uriProtocol);
71 ExitOnFailure(hr, "Failed to determine UriProtocol");
72 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FILE, (int)uriProtocol);
73
74 uri = L"FTP://localhost/";
75 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
76 hr = UriProtocol(uri, &uriProtocol);
77 ExitOnFailure(hr, "Failed to determine UriProtocol");
78 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
79
80 uri = L"ftp://localhost/";
81 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
82 hr = UriProtocol(uri, &uriProtocol);
83 ExitOnFailure(hr, "Failed to determine UriProtocol");
84 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
85
86 uri = L"FtP://localhost/";
87 uriProtocol = URI_PROTOCOL::URI_PROTOCOL_UNKNOWN;
88 hr = UriProtocol(uri, &uriProtocol);
89 ExitOnFailure(hr, "Failed to determine UriProtocol");
90 Assert::Equal((int)URI_PROTOCOL::URI_PROTOCOL_FTP, (int)uriProtocol);
91
92 LExit:
93 ;
94 }
95 };
96}
diff --git a/src/test/DUtilUnitTest/VarHelpers.cpp b/src/test/DUtilUnitTest/VarHelpers.cpp
new file mode 100644
index 00000000..aba69438
--- /dev/null
+++ b/src/test/DUtilUnitTest/VarHelpers.cpp
@@ -0,0 +1,147 @@
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 DutilTests
7{
8 using namespace System;
9 using namespace WixTest;
10
11 void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue)
12 {
13 HRESULT hr = S_OK;
14
15 hr = VarSetString(pVariables, wzVariable, wzValue);
16 NativeAssert::Succeeded(hr, "Failed to set {0} to: {1}", wzVariable, wzValue);
17 }
18
19 void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue)
20 {
21 HRESULT hr = S_OK;
22
23 hr = VarSetNumeric(pVariables, wzVariable, llValue);
24 NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: {1}"), gcnew String(wzVariable), llValue);
25 }
26
27 void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue)
28 {
29 HRESULT hr = S_OK;
30
31 hr = VarSetVersion(pVariables, wzVariable, qwValue);
32 NativeAssert::Succeeded(hr, gcnew String("Failed to set {0} to: 0x{1:X8}"), gcnew String(wzVariable), qwValue);
33 }
34
35 void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue)
36 {
37 HRESULT hr = S_OK;
38 LPWSTR scz = NULL;
39
40 try
41 {
42 hr = VarGetString(pVariables, wzVariable, &scz);
43 NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable);
44 NativeAssert::StringEqual(wzExpectedValue, scz);
45 }
46 finally
47 {
48 ReleaseStr(scz);
49 }
50 }
51
52 void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue)
53 {
54 HRESULT hr = S_OK;
55 LONGLONG llValue = 0;
56
57 hr = VarGetNumeric(pVariables, wzVariable, &llValue);
58 NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable);
59 NativeAssert::Equal(llExpectedValue, llValue);
60 }
61
62 void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue)
63 {
64 HRESULT hr = S_OK;
65 DWORD64 qwValue = 0;
66
67 hr = VarGetVersion(pVariables, wzVariable, &qwValue);
68 NativeAssert::Succeeded(hr, "Failed to get: {0}", wzVariable);
69 NativeAssert::Equal(qwExpectedValue, qwValue);
70 }
71
72 void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue)
73 {
74 HRESULT hr = S_OK;
75 LPWSTR scz = NULL;
76
77 try
78 {
79 hr = VarGetFormatted(pVariables, wzVariable, &scz);
80 NativeAssert::Succeeded(hr, "Failed to get formatted: {0}", wzVariable);
81 NativeAssert::StringEqual(wzExpectedValue, scz);
82 }
83 finally
84 {
85 ReleaseStr(scz);
86 }
87 }
88
89 void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue)
90 {
91 HRESULT hr = S_OK;
92 LPWSTR scz = NULL;
93
94 try
95 {
96 hr = VarFormatString(pVariables, wzIn, &scz, NULL);
97 NativeAssert::Succeeded(hr, "Failed to format string: '{0}'", wzIn);
98 NativeAssert::StringEqual(wzExpectedValue, scz);
99 }
100 finally
101 {
102 ReleaseStr(scz);
103 }
104 }
105
106 void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue)
107 {
108 HRESULT hr = S_OK;
109 LPWSTR scz = NULL;
110
111 try
112 {
113 hr = VarEscapeString(wzIn, &scz);
114 NativeAssert::Succeeded(hr, "Failed to escape string: '{0}'", wzIn);
115 NativeAssert::StringEqual(wzExpectedValue, scz);
116 }
117 finally
118 {
119 ReleaseStr(scz);
120 }
121 }
122
123 bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition)
124 {
125 HRESULT hr = S_OK;
126 BOOL f = FALSE;
127
128 hr = CondEvaluate(pVariables, wzCondition, &f);
129 NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition);
130
131 return f ? true : false;
132 }
133
134 bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition)
135 {
136 HRESULT hr = S_OK;
137 BOOL f = FALSE;
138
139 hr = CondEvaluate(pVariables, wzCondition, &f);
140 if (E_INVALIDDATA != hr)
141 {
142 NativeAssert::Succeeded(hr, "Failed to evaluate condition: '{0}'", wzCondition);
143 }
144
145 return E_INVALIDDATA == hr ? true : false;
146 }
147}
diff --git a/src/test/DUtilUnitTest/VarHelpers.h b/src/test/DUtilUnitTest/VarHelpers.h
new file mode 100644
index 00000000..9b781ce6
--- /dev/null
+++ b/src/test/DUtilUnitTest/VarHelpers.h
@@ -0,0 +1,20 @@
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 DutilTests
6{
7
8void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue);
9void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue);
10void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue);
11void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue);
12void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue);
13void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue);
14void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue);
15void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue);
16void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue);
17bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition);
18bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition);
19
20}
diff --git a/src/test/DUtilUnitTest/VarUtilTest.cpp b/src/test/DUtilUnitTest/VarUtilTest.cpp
new file mode 100644
index 00000000..206310f5
--- /dev/null
+++ b/src/test/DUtilUnitTest/VarUtilTest.cpp
@@ -0,0 +1,532 @@
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
7using namespace System;
8using namespace Xunit;
9using namespace WixTest;
10
11namespace DutilTests
12{
13 typedef struct _VarUtilContext
14 {
15 DWORD dw;
16 LPWSTR scz;
17 } VarUtilContext;
18
19 void FreeValueContext(LPVOID pvContext)
20 {
21 if (pvContext)
22 {
23 MemFree(pvContext);
24 }
25 }
26
27 public ref class VarUtil
28 {
29 public:
30 [NamedFact(Skip = "varutil Not Implemented Yet.")]
31 void VarUtilBasicTest()
32 {
33 HRESULT hr = S_OK;
34 VARIABLES_HANDLE pVariables = NULL;
35
36 try
37 {
38 hr = VarCreate(&pVariables);
39 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
40
41 // set variables
42 VarSetStringHelper(pVariables, L"PROP1", L"VAL1");
43 VarSetNumericHelper(pVariables, L"PROP2", 2);
44 VarSetStringHelper(pVariables, L"PROP5", L"VAL5");
45 VarSetStringHelper(pVariables, L"PROP3", L"VAL3");
46 VarSetStringHelper(pVariables, L"PROP4", L"VAL4");
47 VarSetStringHelper(pVariables, L"PROP6", L"VAL6");
48 VarSetStringHelper(pVariables, L"PROP7", L"7");
49 VarSetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0));
50
51 // set overwritten variables
52 VarSetStringHelper(pVariables, L"OVERWRITTEN_STRING", L"ORIGINAL");
53 VarSetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42);
54
55 VarSetNumericHelper(pVariables, L"OVERWRITTEN_NUMBER", 5);
56 VarSetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW");
57
58 // get and verify variable values
59 VarGetStringHelper(pVariables, L"PROP1", L"VAL1");
60 VarGetNumericHelper(pVariables, L"PROP2", 2);
61 VarGetStringHelper(pVariables, L"PROP2", L"2");
62 VarGetStringHelper(pVariables, L"PROP3", L"VAL3");
63 VarGetStringHelper(pVariables, L"PROP4", L"VAL4");
64 VarGetStringHelper(pVariables, L"PROP5", L"VAL5");
65 VarGetStringHelper(pVariables, L"PROP6", L"VAL6");
66 VarGetNumericHelper(pVariables, L"PROP7", 7);
67 VarGetVersionHelper(pVariables, L"PROP8", MAKEQWORDVERSION(1, 1, 0, 0));
68 VarGetStringHelper(pVariables, L"PROP8", L"1.1.0.0");
69
70 VarGetNumericHelper(pVariables, L"OVERWRITTEN_STRING", 42);
71 VarGetStringHelper(pVariables, L"OVERWRITTEN_NUMBER", L"NEW");
72 }
73 finally
74 {
75 ReleaseVariables(pVariables);
76 }
77 }
78
79 [NamedFact(Skip = "varutil Not Implemented Yet.")]
80 void VarUtilFormatTest()
81 {
82 HRESULT hr = S_OK;
83 VARIABLES_HANDLE pVariables = NULL;
84 LPWSTR scz = NULL;
85 DWORD cch = 0;
86 try
87 {
88 hr = VarCreate(&pVariables);
89 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
90
91 // set variables
92 VarSetStringHelper(pVariables, L"PROP1", L"VAL1");
93 VarSetStringHelper(pVariables, L"PROP2", L"VAL2");
94 VarSetNumericHelper(pVariables, L"PROP3", 3);
95
96 // test string formatting
97 VarFormatStringHelper(pVariables, L"NOPROP", L"NOPROP");
98 VarFormatStringHelper(pVariables, L"[PROP1]", L"VAL1");
99 VarFormatStringHelper(pVariables, L" [PROP1] ", L" VAL1 ");
100 VarFormatStringHelper(pVariables, L"PRE [PROP1]", L"PRE VAL1");
101 VarFormatStringHelper(pVariables, L"[PROP1] POST", L"VAL1 POST");
102 VarFormatStringHelper(pVariables, L"PRE [PROP1] POST", L"PRE VAL1 POST");
103 VarFormatStringHelper(pVariables, L"[PROP1] MID [PROP2]", L"VAL1 MID VAL2");
104 VarFormatStringHelper(pVariables, L"[NONE]", L"");
105 VarFormatStringHelper(pVariables, L"[prop1]", L"");
106 VarFormatStringHelper(pVariables, L"[\\[]", L"[");
107 VarFormatStringHelper(pVariables, L"[\\]]", L"]");
108 VarFormatStringHelper(pVariables, L"[]", L"[]");
109 VarFormatStringHelper(pVariables, L"[NONE", L"[NONE");
110 VarGetFormattedHelper(pVariables, L"PROP2", L"VAL2");
111 VarGetFormattedHelper(pVariables, L"PROP3", L"3");
112
113 hr = VarFormatString(pVariables, L"PRE [PROP1] POST", &scz, &cch);
114 NativeAssert::Succeeded(hr, "Failed to format string.");
115
116 Assert::Equal<DWORD>(lstrlenW(scz), cch);
117
118 hr = VarFormatString(pVariables, L"PRE [PROP1] POST", NULL, &cch);
119 NativeAssert::Succeeded(hr, "Failed to format string.");
120
121 Assert::Equal<DWORD>(lstrlenW(scz), cch);
122 }
123 finally
124 {
125 ReleaseVariables(pVariables);
126 ReleaseStr(scz);
127 }
128 }
129
130 [NamedFact(Skip = "varutil Not Implemented Yet.")]
131 void VarUtilEscapeTest()
132 {
133 // test string escaping
134 VarEscapeStringHelper(L"[", L"[\\[]");
135 VarEscapeStringHelper(L"]", L"[\\]]");
136 VarEscapeStringHelper(L" [TEXT] ", L" [\\[]TEXT[\\]] ");
137 }
138
139 [NamedFact(Skip = "varutil Not Implemented Yet.")]
140 void VarUtilConditionTest()
141 {
142 HRESULT hr = S_OK;
143 VARIABLES_HANDLE pVariables = NULL;
144
145 try
146 {
147 hr = VarCreate(&pVariables);
148 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
149
150 // set variables
151 VarSetStringHelper(pVariables, L"PROP1", L"VAL1");
152 VarSetStringHelper(pVariables, L"PROP2", L"VAL2");
153 VarSetStringHelper(pVariables, L"PROP3", L"VAL3");
154 VarSetStringHelper(pVariables, L"PROP4", L"BEGIN MID END");
155 VarSetNumericHelper(pVariables, L"PROP5", 5);
156 VarSetNumericHelper(pVariables, L"PROP6", 6);
157 VarSetStringHelper(pVariables, L"PROP7", L"");
158 VarSetNumericHelper(pVariables, L"PROP8", 0);
159 VarSetStringHelper(pVariables, L"_PROP9", L"VAL9");
160 VarSetNumericHelper(pVariables, L"PROP10", -10);
161 VarSetNumericHelper(pVariables, L"PROP11", 9223372036854775807ll);
162 VarSetNumericHelper(pVariables, L"PROP12", -9223372036854775808ll);
163 VarSetNumericHelper(pVariables, L"PROP13", 0x00010000);
164 VarSetNumericHelper(pVariables, L"PROP14", 0x00000001);
165 VarSetNumericHelper(pVariables, L"PROP15", 0x00010001);
166 VarSetVersionHelper(pVariables, L"PROP16", MAKEQWORDVERSION(0, 0, 0, 0));
167 VarSetVersionHelper(pVariables, L"PROP17", MAKEQWORDVERSION(1, 0, 0, 0));
168 VarSetVersionHelper(pVariables, L"PROP18", MAKEQWORDVERSION(1, 1, 0, 0));
169 VarSetVersionHelper(pVariables, L"PROP19", MAKEQWORDVERSION(1, 1, 1, 0));
170 VarSetVersionHelper(pVariables, L"PROP20", MAKEQWORDVERSION(1, 1, 1, 1));
171 VarSetNumericHelper(pVariables, L"vPROP21", 1);
172 VarSetVersionHelper(pVariables, L"PROP22", MAKEQWORDVERSION(65535, 65535, 65535, 65535));
173 VarSetStringHelper(pVariables, L"PROP23", L"1.1.1");
174
175 // test conditions
176 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1"));
177 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5"));
178 Assert::False(EvaluateConditionHelper(pVariables, L"PROP7"));
179 Assert::False(EvaluateConditionHelper(pVariables, L"PROP8"));
180 Assert::True(EvaluateConditionHelper(pVariables, L"_PROP9"));
181 Assert::False(EvaluateConditionHelper(pVariables, L"PROP16"));
182 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17"));
183
184 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\""));
185 Assert::False(EvaluateConditionHelper(pVariables, L"NONE = \"NOT\""));
186 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 <> \"VAL1\""));
187 Assert::True(EvaluateConditionHelper(pVariables, L"NONE <> \"NOT\""));
188
189 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 ~= \"val1\""));
190 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"val1\""));
191 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 ~<> \"val1\""));
192 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> \"val1\""));
193
194 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 = 5"));
195 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 = 0"));
196 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <> 5"));
197 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <> 0"));
198
199 Assert::True(EvaluateConditionHelper(pVariables, L"PROP10 = -10"));
200 Assert::False(EvaluateConditionHelper(pVariables, L"PROP10 <> -10"));
201
202 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1"));
203 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 = v0"));
204 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 <> v1"));
205 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 <> v0"));
206
207 Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 = v0"));
208 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 = v1"));
209 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 = v1.1"));
210 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 = v1.1.1"));
211 Assert::True(EvaluateConditionHelper(pVariables, L"PROP20 = v1.1.1.1"));
212 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.0"));
213 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP20 = v1.1.1.1.1"));
214 Assert::True(EvaluateConditionHelper(pVariables, L"vPROP21 = 1"));
215 Assert::True(EvaluateConditionHelper(pVariables, L"PROP23 = v1.1.1"));
216 Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 = PROP23"));
217 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 <> v1.1.1"));
218 Assert::True(EvaluateConditionHelper(pVariables, L"v1.1.1 <> PROP1"));
219
220 Assert::False(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775806"));
221 Assert::True(EvaluateConditionHelper(pVariables, L"PROP11 = 9223372036854775807"));
222 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 9223372036854775808"));
223 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP11 = 92233720368547758070000"));
224
225 Assert::False(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775807"));
226 Assert::True(EvaluateConditionHelper(pVariables, L"PROP12 = -9223372036854775808"));
227 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -9223372036854775809"));
228 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP12 = -92233720368547758080000"));
229
230 Assert::True(EvaluateConditionHelper(pVariables, L"PROP22 = v65535.65535.65535.65535"));
231 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65536.65535.65535.65535"));
232 Assert::True(EvaluateFailureConditionHelper(pVariables, L"PROP22 = v65535.655350000.65535.65535"));
233
234 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 < 6"));
235 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 < 5"));
236 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 > 4"));
237 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 > 5"));
238 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 6"));
239 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 <= 5"));
240 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 <= 4"));
241 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 4"));
242 Assert::True(EvaluateConditionHelper(pVariables, L"PROP5 >= 5"));
243 Assert::False(EvaluateConditionHelper(pVariables, L"PROP5 >= 6"));
244
245 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 << \"BEGIN\""));
246 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 << \"END\""));
247 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >> \"END\""));
248 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >> \"BEGIN\""));
249 Assert::True(EvaluateConditionHelper(pVariables, L"PROP4 >< \"MID\""));
250 Assert::False(EvaluateConditionHelper(pVariables, L"PROP4 >< \"NONE\""));
251
252 Assert::True(EvaluateConditionHelper(pVariables, L"PROP16 < v1.1"));
253 Assert::False(EvaluateConditionHelper(pVariables, L"PROP16 < v0"));
254 Assert::True(EvaluateConditionHelper(pVariables, L"PROP17 > v0.12"));
255 Assert::False(EvaluateConditionHelper(pVariables, L"PROP17 > v1"));
256 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.0"));
257 Assert::True(EvaluateConditionHelper(pVariables, L"PROP18 >= v1.1"));
258 Assert::False(EvaluateConditionHelper(pVariables, L"PROP18 >= v2.1"));
259 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1234.1"));
260 Assert::True(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.1.1"));
261 Assert::False(EvaluateConditionHelper(pVariables, L"PROP19 <= v1.0.123"));
262
263 Assert::True(EvaluateConditionHelper(pVariables, L"PROP6 = \"6\""));
264 Assert::True(EvaluateConditionHelper(pVariables, L"\"6\" = PROP6"));
265 Assert::False(EvaluateConditionHelper(pVariables, L"PROP6 = \"ABC\""));
266 Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6"));
267 Assert::False(EvaluateConditionHelper(pVariables, L"\"ABC\" = PROP6"));
268
269 Assert::True(EvaluateConditionHelper(pVariables, L"PROP13 << 1"));
270 Assert::False(EvaluateConditionHelper(pVariables, L"PROP13 << 0"));
271 Assert::True(EvaluateConditionHelper(pVariables, L"PROP14 >> 1"));
272 Assert::False(EvaluateConditionHelper(pVariables, L"PROP14 >> 0"));
273 Assert::True(EvaluateConditionHelper(pVariables, L"PROP15 >< 65537"));
274 Assert::False(EvaluateConditionHelper(pVariables, L"PROP15 >< 0"));
275
276 Assert::False(EvaluateConditionHelper(pVariables, L"NOT PROP1"));
277 Assert::True(EvaluateConditionHelper(pVariables, L"NOT (PROP1 <> \"VAL1\")"));
278
279 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
280 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
281 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"VAL2\""));
282 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" AND PROP2 = \"NOT\""));
283
284 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"VAL2\""));
285 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" OR PROP2 = \"NOT\""));
286 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"VAL2\""));
287 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"NOT\" OR PROP2 = \"NOT\""));
288
289 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"VAL2\" OR PROP3 = \"NOT\""));
290 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"VAL3\""));
291 Assert::False(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND PROP2 = \"NOT\" OR PROP3 = \"NOT\""));
292 Assert::True(EvaluateConditionHelper(pVariables, L"PROP1 = \"VAL1\" AND (PROP2 = \"NOT\" OR PROP3 = \"VAL3\")"));
293 Assert::True(EvaluateConditionHelper(pVariables, L"(PROP1 = \"VAL1\" AND PROP2 = \"VAL2\") OR PROP3 = \"NOT\""));
294
295 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"VAL2\""));
296 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"VAL3\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
297 Assert::False(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR PROP1 = \"VAL1\" AND PROP2 = \"NOT\""));
298 Assert::True(EvaluateConditionHelper(pVariables, L"(PROP3 = \"NOT\" OR PROP1 = \"VAL1\") AND PROP2 = \"VAL2\""));
299 Assert::True(EvaluateConditionHelper(pVariables, L"PROP3 = \"NOT\" OR (PROP1 = \"VAL1\" AND PROP2 = \"VAL2\")"));
300
301 Assert::True(EvaluateFailureConditionHelper(pVariables, L"="));
302 Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1"));
303 Assert::True(EvaluateFailureConditionHelper(pVariables, L"(PROP1 = \""));
304 Assert::True(EvaluateFailureConditionHelper(pVariables, L"1A"));
305 Assert::True(EvaluateFailureConditionHelper(pVariables, L"*"));
306
307 Assert::True(EvaluateFailureConditionHelper(pVariables, L"1 == 1"));
308 }
309 finally
310 {
311 ReleaseVariables(pVariables);
312 }
313 }
314
315 [NamedFact(Skip = "varutil Not Implemented Yet.")]
316 void VarUtilValueTest()
317 {
318 HRESULT hr = S_OK;
319 VARIABLES_HANDLE pVariables = NULL;
320 VARIABLE_VALUE values[8];
321
322 try
323 {
324 hr = VarCreate(&pVariables);
325 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
326
327 // set variables
328 InitNumericValue(pVariables, values + 0, 2, FALSE, 1, L"PROP1");
329 InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 2, L"PROP2");
330 InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 3, L"PROP3");
331 InitNoneValue(pVariables, values + 3, FALSE, 4, L"PROP4");
332 InitNoneValue(pVariables, values + 4, TRUE, 5, L"PROP5");
333 InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 6, L"PROP6");
334 InitStringValue(pVariables, values + 6, L"7", TRUE, 7, L"PROP7");
335 InitNumericValue(pVariables, values + 7, 11, TRUE, 8, L"PROP8");
336
337 for (DWORD i = 0; i < 8; i++)
338 {
339 VerifyValue(pVariables, values + i);
340 }
341 }
342 finally
343 {
344 VarDestroy(pVariables, FreeValueContext);
345 }
346 }
347
348 [NamedFact(Skip = "varutil Not Implemented Yet.")]
349 void VarUtilEnumTest()
350 {
351 HRESULT hr = S_OK;
352 const DWORD dwIndex = 8;
353 VARIABLES_HANDLE pVariables = NULL;
354 VARIABLE_ENUM_HANDLE pEnum = NULL;
355 VARIABLE_VALUE values[dwIndex];
356 VARIABLE_VALUE* pValue = NULL;
357
358 try
359 {
360 hr = VarCreate(&pVariables);
361 NativeAssert::Succeeded(hr, "Failed to initialize variables.");
362
363 hr = VarStartEnum(pVariables, &pEnum, &pValue);
364 NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS);
365
366 // set variables
367 InitNumericValue(pVariables, values + 0, 2, FALSE, 0, L"PROP1");
368 InitStringValue(pVariables, values + 1, L"VAL2", FALSE, 0, L"PROP2");
369 InitVersionValue(pVariables, values + 2, MAKEQWORDVERSION(1, 1, 0, 0), FALSE, 0, L"PROP3");
370 InitNoneValue(pVariables, values + 3, FALSE, 0, L"PROP4");
371 InitNoneValue(pVariables, values + 4, TRUE, 0, L"PROP5");
372 InitVersionValue(pVariables, values + 5, MAKEQWORDVERSION(1, 1, 1, 0), TRUE, 0, L"PROP6");
373 InitStringValue(pVariables, values + 6, L"7", TRUE, 0, L"PROP7");
374 InitNumericValue(pVariables, values + 7, 11, TRUE, 0, L"PROP8");
375
376 hr = VarStartEnum(pVariables, &pEnum, &pValue);
377
378 for (DWORD i = dwIndex - 1; i; --i)
379 {
380 NativeAssert::ValidReturnCode(hr, S_OK);
381
382 VarUtilContext* pContext = reinterpret_cast<VarUtilContext*>(pValue->pvContext);
383 pContext->dw += 1;
384
385 hr = VarNextVariable(pEnum, &pValue);
386 }
387
388 NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS);
389
390 for (DWORD j = 0; j < dwIndex; j++)
391 {
392 VarUtilContext* pContext = reinterpret_cast<VarUtilContext*>(values[j].pvContext);
393 NativeAssert::Equal<DWORD>(1, pContext->dw);
394 }
395
396 VarFinishEnum(pEnum);
397 pEnum = NULL;
398
399 hr = VarStartEnum(pVariables, &pEnum, &pValue);
400
401 for (DWORD i = dwIndex - 1; i; --i)
402 {
403 NativeAssert::ValidReturnCode(hr, S_OK);
404
405 VarUtilContext* pContext = reinterpret_cast<VarUtilContext*>(pValue->pvContext);
406 pContext->dw += 1;
407
408 hr = VarNextVariable(pEnum, &pValue);
409 }
410
411 NativeAssert::ValidReturnCode(hr, E_NOMOREITEMS);
412
413 for (DWORD j = 0; j < dwIndex; j++)
414 {
415 VarUtilContext* pContext = reinterpret_cast<VarUtilContext*>(values[j].pvContext);
416 NativeAssert::Equal<DWORD>(2, pContext->dw);
417 }
418 }
419 finally
420 {
421 VarFinishEnum(pEnum);
422 ReleaseVariableValue(pValue);
423 VarDestroy(pVariables, FreeValueContext);
424 }
425 }
426
427 private:
428 void InitNoneValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, BOOL fHidden, DWORD dw, LPCWSTR wz)
429 {
430 pValue->type = VARIABLE_VALUE_TYPE_NONE;
431 pValue->fHidden = fHidden;
432
433 InitValueContext(pValue, dw, wz);
434
435 HRESULT hr = VarSetValue(pVariables, wz, pValue);
436 NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz);
437 }
438
439 void InitNumericValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LONGLONG llValue, BOOL fHidden, DWORD dw, LPCWSTR wz)
440 {
441 pValue->type = VARIABLE_VALUE_TYPE_NUMERIC;
442 pValue->fHidden = fHidden;
443
444 pValue->llValue = llValue;
445
446 InitValueContext(pValue, dw, wz);
447
448 HRESULT hr = VarSetValue(pVariables, wz, pValue);
449 NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz);
450 }
451
452 void InitStringValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, LPWSTR wzValue, BOOL fHidden, DWORD dw, LPCWSTR wz)
453 {
454 pValue->type = VARIABLE_VALUE_TYPE_STRING;
455 pValue->fHidden = fHidden;
456
457 HRESULT hr = StrAllocString(&pValue->sczValue, wzValue, 0);
458 NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wzValue);
459
460 InitValueContext(pValue, dw, wz);
461
462 hr = VarSetValue(pVariables, wz, pValue);
463 NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz);
464 }
465
466 void InitVersionValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pValue, DWORD64 qwValue, BOOL fHidden, DWORD dw, LPCWSTR wz)
467 {
468 pValue->type = VARIABLE_VALUE_TYPE_VERSION;
469 pValue->fHidden = fHidden;
470
471 pValue->qwValue = qwValue;
472
473 InitValueContext(pValue, dw, wz);
474
475 HRESULT hr = VarSetValue(pVariables, wz, pValue);
476 NativeAssert::Succeeded(hr, "Failed to set value for variable {0}", wz);
477 }
478
479 void InitValueContext(VARIABLE_VALUE* pValue, DWORD dw, LPCWSTR wz)
480 {
481 pValue->pvContext = MemAlloc(sizeof(VarUtilContext), TRUE);
482 VarUtilContext* pContext = reinterpret_cast<VarUtilContext*>(pValue->pvContext);
483 if (!pContext)
484 {
485 throw gcnew OutOfMemoryException();
486 }
487
488 pContext->dw = dw;
489
490 HRESULT hr = StrAllocString(&pContext->scz, wz, 0);
491 NativeAssert::Succeeded(hr, "Failed to alloc string: {0}", wz);
492 }
493
494 void VerifyValue(VARIABLES_HANDLE pVariables, VARIABLE_VALUE* pExpectedValue)
495 {
496 VARIABLE_VALUE* pActualValue = NULL;
497
498 try
499 {
500 VarUtilContext* pExpectedContext = reinterpret_cast<VarUtilContext*>(pExpectedValue->pvContext);
501 NativeAssert::True(NULL != pExpectedContext);
502
503 HRESULT hr = VarGetValue(pVariables, pExpectedContext->scz, &pActualValue);
504 NativeAssert::Succeeded(hr, "Failed to get value: {0}", pExpectedContext->scz);
505
506 NativeAssert::Equal<DWORD>(pExpectedValue->type, pActualValue->type);
507 NativeAssert::InRange<DWORD>(pExpectedValue->type, VARIABLE_VALUE_TYPE_NONE, VARIABLE_VALUE_TYPE_STRING);
508
509 switch (pExpectedValue->type)
510 {
511 case VARIABLE_VALUE_TYPE_NONE:
512 case VARIABLE_VALUE_TYPE_VERSION:
513 NativeAssert::Equal(pExpectedValue->qwValue, pActualValue->qwValue);
514 break;
515 case VARIABLE_VALUE_TYPE_NUMERIC:
516 NativeAssert::Equal(pExpectedValue->llValue, pActualValue->llValue);
517 break;
518 case VARIABLE_VALUE_TYPE_STRING:
519 NativeAssert::StringEqual(pExpectedValue->sczValue, pActualValue->sczValue);
520 break;
521 }
522
523 NativeAssert::Equal(pExpectedValue->fHidden, pActualValue->fHidden);
524 NativeAssert::True(pExpectedValue->pvContext == pActualValue->pvContext);
525 }
526 finally
527 {
528 ReleaseVariableValue(pActualValue);
529 }
530 }
531 };
532}
diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h
new file mode 100644
index 00000000..a52db56d
--- /dev/null
+++ b/src/test/DUtilUnitTest/error.h
@@ -0,0 +1,8 @@
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
3const int ERROR_STRING_BUFFER = 1024;
4
5static char szMsg[ERROR_STRING_BUFFER];
6static WCHAR wzMsg[ERROR_STRING_BUFFER];
7
8#define ExitTrace(x, f, ...) { HRESULT hrTemp = x; hr = ::StringCchPrintfA(szMsg, countof(szMsg), f, __VA_ARGS__); MultiByteToWideChar(CP_ACP, 0, szMsg, -1, wzMsg, countof(wzMsg)); throw gcnew System::Exception(System::String::Format("hr = 0x{0:X8}, message = {1}", hrTemp, gcnew System::String(wzMsg))); }
diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config
new file mode 100644
index 00000000..17dcb258
--- /dev/null
+++ b/src/test/DUtilUnitTest/packages.config
@@ -0,0 +1,7 @@
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<packages>
6 <package id="xunit" version="1.9.1" targetFramework="net40" />
7</packages>
diff --git a/src/test/DUtilUnitTest/precomp.cpp b/src/test/DUtilUnitTest/precomp.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/test/DUtilUnitTest/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/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h
new file mode 100644
index 00000000..aa8f7de6
--- /dev/null
+++ b/src/test/DUtilUnitTest/precomp.h
@@ -0,0 +1,30 @@
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 <strsafe.h>
7#include <ShlObj.h>
8
9// Include error.h before dutil.h
10#include "error.h"
11#include <dutil.h>
12
13#include <dictutil.h>
14#include <dirutil.h>
15#include <fileutil.h>
16#include <guidutil.h>
17#include <iniutil.h>
18#include <memutil.h>
19#include <pathutil.h>
20#include <strutil.h>
21#include <monutil.h>
22#include <regutil.h>
23#include <uriutil.h>
24#include <varutil.h>
25#include <condutil.h>
26
27#include "VarHelpers.h"
28
29#pragma managed
30#include <vcclr.h>
diff --git a/src/test/DUtilUnitTest/resource.h b/src/test/DUtilUnitTest/resource.h
new file mode 100644
index 00000000..bdf252f6
--- /dev/null
+++ b/src/test/DUtilUnitTest/resource.h
@@ -0,0 +1,2 @@
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