Import DUtilUnitTest from old wix4 repo.
Sean Hall committed
Jul 11, 2020 at 21:11 UTC
9c4b5559ccb55491fe68b0096d1be0496fd6fcc7
25 files changed
+3794
src/test/DUtilUnitTest/AssemblyInfo.cpp
new
+12
@@ -0,0 +1,12 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
4
+
5
+using namespace System::Reflection;
6
+using namespace System::Runtime::CompilerServices;
7
+using namespace System::Runtime::InteropServices;
8
+
9
+[assembly: AssemblyTitleAttribute("Windows Installer XML Dutil unit tests")];
10
+[assembly: AssemblyDescriptionAttribute("Dutil unit tests")];
11
+[assembly: AssemblyCultureAttribute("")];
12
+[assembly: ComVisible(false)];
src/test/DUtilUnitTest/CondUtilTest.cpp
new
+190
@@ -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
+
5
+namespace 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
+}
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
new
+73
@@ -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>
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
new
+77
@@ -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
src/test/DUtilUnitTest/DictUtilTest.cpp
new
+187
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+const DWORD numIterations = 100000;
10
+
11
+namespace 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
+}
src/test/DUtilUnitTest/DirUtilTests.cpp
new
+70
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/FileUtilTest.cpp
new
+118
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/GuidUtilTest.cpp
new
+60
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/IniUtilTest.cpp
new
+342
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+typedef HRESULT (__clrcall *IniFormatParameters)(
10
+ INI_HANDLE
11
+ );
12
+
13
+namespace 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
+}
src/test/DUtilUnitTest/MemUtilTest.cpp
new
+491
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/MonUtilTest.cpp
new
+487
@@ -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
+
6
+using namespace System;
7
+using namespace System::Collections::Generic;
8
+using namespace System::Runtime::InteropServices;
9
+using namespace Xunit;
10
+using namespace WixTest;
11
+
12
+namespace 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
+}
src/test/DUtilUnitTest/NativeAssert.h
new
+83
@@ -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
+
5
+namespace 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
+}
src/test/DUtilUnitTest/PathUtilTest.cpp
new
+80
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/SceUtilTest.cpp
new
+488
@@ -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
+
8
+using namespace System;
9
+using namespace Xunit;
10
+using namespace WixTest;
11
+
12
+#define ASSIGN_INDEX_STRUCT(a, b, c) {a.wzName = c; a.rgColumns = b; a.cColumns = countof(b);};
13
+
14
+namespace 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
+}
src/test/DUtilUnitTest/StrUtilTest.cpp
new
+184
@@ -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
+
5
+using namespace System;
6
+using namespace Xunit;
7
+using namespace WixTest;
8
+
9
+namespace 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
+}
src/test/DUtilUnitTest/UnitTest.rc
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#define VER_APP
4
+#define VER_ORIGINAL_FILENAME "UnitTest.dll"
5
+#define VER_INTERNAL_NAME "setup"
6
+#define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests"
7
+#include "wix.rc"
src/test/DUtilUnitTest/UriUtilTest.cpp
new
+96
@@ -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
+
5
+using namespace System;
6
+using namespace System::Text;
7
+using namespace System::Collections::Generic;
8
+using namespace Xunit;
9
+
10
+namespace 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
+}
src/test/DUtilUnitTest/VarHelpers.cpp
new
+147
@@ -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
+
6
+namespace 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
+}
src/test/DUtilUnitTest/VarHelpers.h
new
+20
@@ -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
+
5
+namespace DutilTests
6
+{
7
+
8
+void VarSetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzValue);
9
+void VarSetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llValue);
10
+void VarSetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwValue);
11
+void VarGetStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue);
12
+void VarGetNumericHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LONGLONG llExpectedValue);
13
+void VarGetVersionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, DWORD64 qwExpectedValue);
14
+void VarGetFormattedHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzVariable, LPCWSTR wzExpectedValue);
15
+void VarFormatStringHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzIn, LPCWSTR wzExpectedValue);
16
+void VarEscapeStringHelper(LPCWSTR wzIn, LPCWSTR wzExpectedValue);
17
+bool EvaluateConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition);
18
+bool EvaluateFailureConditionHelper(VARIABLES_HANDLE pVariables, LPCWSTR wzCondition);
19
+
20
+}
src/test/DUtilUnitTest/VarUtilTest.cpp
new
+532
@@ -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
+
7
+using namespace System;
8
+using namespace Xunit;
9
+using namespace WixTest;
10
+
11
+namespace 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
+}
src/test/DUtilUnitTest/error.h
new
+8
@@ -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
+
3
+const int ERROR_STRING_BUFFER = 1024;
4
+
5
+static char szMsg[ERROR_STRING_BUFFER];
6
+static 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))); }
src/test/DUtilUnitTest/packages.config
new
+7
@@ -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>
src/test/DUtilUnitTest/precomp.cpp
new
+3
@@ -0,0 +1,3 @@
1
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
+
3
+#include "precomp.h"
src/test/DUtilUnitTest/precomp.h
new
+30
@@ -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>
src/test/DUtilUnitTest/resource.h
new
+2
@@ -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
+