diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2020-07-11 21:11:30 +1000 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2020-07-12 11:51:29 +1000 |
commit | 9c4b5559ccb55491fe68b0096d1be0496fd6fcc7 (patch) | |
tree | faa51204fc2e6e3dfd1b00ae03c7184c4fb0b4bf | |
parent | f651268309263fa67fa84caf9fb6dbebb5c900d9 (diff) | |
download | wix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.tar.gz wix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.tar.bz2 wix-9c4b5559ccb55491fe68b0096d1be0496fd6fcc7.zip |
Import DUtilUnitTest from old wix4 repo.
25 files changed, 3794 insertions, 0 deletions
diff --git a/src/test/DUtilUnitTest/AssemblyInfo.cpp b/src/test/DUtilUnitTest/AssemblyInfo.cpp new file mode 100644 index 00000000..2d527910 --- /dev/null +++ b/src/test/DUtilUnitTest/AssemblyInfo.cpp | |||
@@ -0,0 +1,12 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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)]; | ||
diff --git a/src/test/DUtilUnitTest/CondUtilTest.cpp b/src/test/DUtilUnitTest/CondUtilTest.cpp new file mode 100644 index 00000000..c808363d --- /dev/null +++ b/src/test/DUtilUnitTest/CondUtilTest.cpp | |||
@@ -0,0 +1,190 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj new file mode 100644 index 00000000..292cf28a --- /dev/null +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj | |||
@@ -0,0 +1,73 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
3 | |||
4 | |||
5 | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
6 | <ItemGroup Label="ProjectConfigurations"> | ||
7 | <ProjectConfiguration Include="Debug|Win32"> | ||
8 | <Configuration>Debug</Configuration> | ||
9 | <Platform>Win32</Platform> | ||
10 | </ProjectConfiguration> | ||
11 | <ProjectConfiguration Include="Release|Win32"> | ||
12 | <Configuration>Release</Configuration> | ||
13 | <Platform>Win32</Platform> | ||
14 | </ProjectConfiguration> | ||
15 | </ItemGroup> | ||
16 | <PropertyGroup Label="Globals"> | ||
17 | <ProjectTypes>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectTypes> | ||
18 | <ProjectGuid>{AB7EE608-E5FB-42A5-831F-0DEEEA141223}</ProjectGuid> | ||
19 | <RootNamespace>DUtilUnitTests</RootNamespace> | ||
20 | <Keyword>ManagedCProj</Keyword> | ||
21 | <ConfigurationType>DynamicLibrary</ConfigurationType> | ||
22 | <CharacterSet>Unicode</CharacterSet> | ||
23 | <CLRSupport>true</CLRSupport> | ||
24 | </PropertyGroup> | ||
25 | <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.props" /> | ||
26 | <PropertyGroup> | ||
27 | <ProjectAdditionalIncludeDirectories>$(WixRoot)src\libs\dutil\inc</ProjectAdditionalIncludeDirectories> | ||
28 | <ProjectAdditionalLinkLibraries>rpcrt4.lib;dutil.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries> | ||
29 | </PropertyGroup> | ||
30 | <ItemGroup> | ||
31 | <ClCompile Include="AssemblyInfo.cpp" /> | ||
32 | <ClCompile Include="CondUtilTest.cpp" /> | ||
33 | <ClCompile Include="DictUtilTest.cpp" /> | ||
34 | <ClCompile Include="DirUtilTests.cpp" /> | ||
35 | <ClCompile Include="FileUtilTest.cpp" /> | ||
36 | <ClCompile Include="GuidUtilTest.cpp" /> | ||
37 | <ClCompile Include="IniUtilTest.cpp" /> | ||
38 | <ClCompile Include="MemUtilTest.cpp" /> | ||
39 | <ClCompile Include="MonUtilTest.cpp" /> | ||
40 | <ClCompile Include="PathUtilTest.cpp" /> | ||
41 | <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " /> | ||
42 | <ClCompile Include="StrUtilTest.cpp" /> | ||
43 | <ClCompile Include="UriUtilTest.cpp" /> | ||
44 | <ClCompile Include="VarHelpers.cpp" /> | ||
45 | <ClCompile Include="VarUtilTest.cpp" /> | ||
46 | </ItemGroup> | ||
47 | <ItemGroup> | ||
48 | <ClInclude Include="precomp.h" /> | ||
49 | <ClInclude Include="error.h" /> | ||
50 | <ClInclude Include="VarHelpers.h" /> | ||
51 | </ItemGroup> | ||
52 | <ItemGroup> | ||
53 | <ResourceCompile Include="UnitTest.rc" /> | ||
54 | </ItemGroup> | ||
55 | <ItemGroup> | ||
56 | <Reference Include="System" /> | ||
57 | <Reference Include="xunit"> | ||
58 | <HintPath>$(XunitPath)\xunit.dll</HintPath> | ||
59 | </Reference> | ||
60 | </ItemGroup> | ||
61 | <ItemGroup> | ||
62 | <ProjectReference Include="..\..\..\..\src\libs\dutil\dutil.vcxproj" /> | ||
63 | <ProjectReference Include="..\..\WixCppCliTestTools\WixCppCliTestTools.vcxproj"> | ||
64 | <Project>{95BABD97-FBDB-453A-AF8A-FA031A07B599}</Project> | ||
65 | <Name>WixCppCliTestTools</Name> | ||
66 | </ProjectReference> | ||
67 | <ProjectReference Include="..\..\WixTestTools\WixTestTools.csproj"> | ||
68 | <Project>{55CB1042-647B-4347-9876-3EA607AF8DCE}</Project> | ||
69 | <Name>WixTestTools</Name> | ||
70 | </ProjectReference> | ||
71 | </ItemGroup> | ||
72 | <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), wix.proj))\tools\WixBuild.targets" /> | ||
73 | </Project> | ||
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters new file mode 100644 index 00000000..a83db35d --- /dev/null +++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters | |||
@@ -0,0 +1,77 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
3 | <ItemGroup> | ||
4 | <Filter Include="Source Files"> | ||
5 | <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> | ||
6 | <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> | ||
7 | </Filter> | ||
8 | <Filter Include="Header Files"> | ||
9 | <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> | ||
10 | <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> | ||
11 | </Filter> | ||
12 | <Filter Include="Resource Files"> | ||
13 | <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | ||
14 | <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | ||
15 | </Filter> | ||
16 | </ItemGroup> | ||
17 | <ItemGroup> | ||
18 | <ClCompile Include="AssemblyInfo.cpp"> | ||
19 | <Filter>Source Files</Filter> | ||
20 | </ClCompile> | ||
21 | <ClCompile Include="CondUtilTest.cpp"> | ||
22 | <Filter>Source Files</Filter> | ||
23 | </ClCompile> | ||
24 | <ClCompile Include="DictUtilTest.cpp"> | ||
25 | <Filter>Source Files</Filter> | ||
26 | </ClCompile> | ||
27 | <ClCompile Include="DirUtilTests.cpp"> | ||
28 | <Filter>Source Files</Filter> | ||
29 | </ClCompile> | ||
30 | <ClCompile Include="FileUtilTest.cpp"> | ||
31 | <Filter>Source Files</Filter> | ||
32 | </ClCompile> | ||
33 | <ClCompile Include="GuidUtilTest.cpp"> | ||
34 | <Filter>Source Files</Filter> | ||
35 | </ClCompile> | ||
36 | <ClCompile Include="IniUtilTest.cpp"> | ||
37 | <Filter>Source Files</Filter> | ||
38 | </ClCompile> | ||
39 | <ClCompile Include="MemUtilTest.cpp"> | ||
40 | <Filter>Source Files</Filter> | ||
41 | </ClCompile> | ||
42 | <ClCompile Include="MonUtilTest.cpp"> | ||
43 | <Filter>Source Files</Filter> | ||
44 | </ClCompile> | ||
45 | <ClCompile Include="PathUtilTest.cpp"> | ||
46 | <Filter>Source Files</Filter> | ||
47 | </ClCompile> | ||
48 | <ClCompile Include="StrUtilTest.cpp"> | ||
49 | <Filter>Source Files</Filter> | ||
50 | </ClCompile> | ||
51 | <ClCompile Include="UriUtilTest.cpp"> | ||
52 | <Filter>Source Files</Filter> | ||
53 | </ClCompile> | ||
54 | <ClCompile Include="VarHelpers.cpp"> | ||
55 | <Filter>Source Files</Filter> | ||
56 | </ClCompile> | ||
57 | <ClCompile Include="VarUtilTest.cpp"> | ||
58 | <Filter>Source Files</Filter> | ||
59 | </ClCompile> | ||
60 | </ItemGroup> | ||
61 | <ItemGroup> | ||
62 | <ResourceCompile Include="UnitTest.rc"> | ||
63 | <Filter>Resource Files</Filter> | ||
64 | </ResourceCompile> | ||
65 | </ItemGroup> | ||
66 | <ItemGroup> | ||
67 | <ClInclude Include="precomp.h"> | ||
68 | <Filter>Header Files</Filter> | ||
69 | </ClInclude> | ||
70 | <ClInclude Include="error.h"> | ||
71 | <Filter>Header Files</Filter> | ||
72 | </ClInclude> | ||
73 | <ClInclude Include="VarHelpers.h"> | ||
74 | <Filter>Header Files</Filter> | ||
75 | </ClInclude> | ||
76 | </ItemGroup> | ||
77 | </Project> \ No newline at end of file | ||
diff --git a/src/test/DUtilUnitTest/DictUtilTest.cpp b/src/test/DUtilUnitTest/DictUtilTest.cpp new file mode 100644 index 00000000..fd8a5953 --- /dev/null +++ b/src/test/DUtilUnitTest/DictUtilTest.cpp | |||
@@ -0,0 +1,187 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/DirUtilTests.cpp b/src/test/DUtilUnitTest/DirUtilTests.cpp new file mode 100644 index 00000000..a965c3d5 --- /dev/null +++ b/src/test/DUtilUnitTest/DirUtilTests.cpp | |||
@@ -0,0 +1,70 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/FileUtilTest.cpp b/src/test/DUtilUnitTest/FileUtilTest.cpp new file mode 100644 index 00000000..41638bdb --- /dev/null +++ b/src/test/DUtilUnitTest/FileUtilTest.cpp | |||
@@ -0,0 +1,118 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/GuidUtilTest.cpp b/src/test/DUtilUnitTest/GuidUtilTest.cpp new file mode 100644 index 00000000..d0ea9a89 --- /dev/null +++ b/src/test/DUtilUnitTest/GuidUtilTest.cpp | |||
@@ -0,0 +1,60 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/IniUtilTest.cpp b/src/test/DUtilUnitTest/IniUtilTest.cpp new file mode 100644 index 00000000..e28f357e --- /dev/null +++ b/src/test/DUtilUnitTest/IniUtilTest.cpp | |||
@@ -0,0 +1,342 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/MemUtilTest.cpp b/src/test/DUtilUnitTest/MemUtilTest.cpp new file mode 100644 index 00000000..6dec9682 --- /dev/null +++ b/src/test/DUtilUnitTest/MemUtilTest.cpp | |||
@@ -0,0 +1,491 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/MonUtilTest.cpp b/src/test/DUtilUnitTest/MonUtilTest.cpp new file mode 100644 index 00000000..a6ed32f1 --- /dev/null +++ b/src/test/DUtilUnitTest/MonUtilTest.cpp | |||
@@ -0,0 +1,487 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | #undef RemoveDirectory | ||
5 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/NativeAssert.h b/src/test/DUtilUnitTest/NativeAssert.h new file mode 100644 index 00000000..b10910c0 --- /dev/null +++ b/src/test/DUtilUnitTest/NativeAssert.h | |||
@@ -0,0 +1,83 @@ | |||
1 | #pragma once | ||
2 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
3 | |||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/PathUtilTest.cpp b/src/test/DUtilUnitTest/PathUtilTest.cpp new file mode 100644 index 00000000..13ec3be3 --- /dev/null +++ b/src/test/DUtilUnitTest/PathUtilTest.cpp | |||
@@ -0,0 +1,80 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/SceUtilTest.cpp b/src/test/DUtilUnitTest/SceUtilTest.cpp new file mode 100644 index 00000000..75b9222a --- /dev/null +++ b/src/test/DUtilUnitTest/SceUtilTest.cpp | |||
@@ -0,0 +1,488 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | #include <sqlce_oledb.h> | ||
6 | #include <sceutil.h> | ||
7 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/StrUtilTest.cpp b/src/test/DUtilUnitTest/StrUtilTest.cpp new file mode 100644 index 00000000..406f2f23 --- /dev/null +++ b/src/test/DUtilUnitTest/StrUtilTest.cpp | |||
@@ -0,0 +1,184 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/UnitTest.rc b/src/test/DUtilUnitTest/UnitTest.rc new file mode 100644 index 00000000..bf68360a --- /dev/null +++ b/src/test/DUtilUnitTest/UnitTest.rc | |||
@@ -0,0 +1,7 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #define VER_APP | ||
4 | #define VER_ORIGINAL_FILENAME "UnitTest.dll" | ||
5 | #define VER_INTERNAL_NAME "setup" | ||
6 | #define VER_FILE_DESCRIPTION "WiX Toolset Bootstrapper unit tests" | ||
7 | #include "wix.rc" | ||
diff --git a/src/test/DUtilUnitTest/UriUtilTest.cpp b/src/test/DUtilUnitTest/UriUtilTest.cpp new file mode 100644 index 00000000..220b3ff5 --- /dev/null +++ b/src/test/DUtilUnitTest/UriUtilTest.cpp | |||
@@ -0,0 +1,96 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/VarHelpers.cpp b/src/test/DUtilUnitTest/VarHelpers.cpp new file mode 100644 index 00000000..aba69438 --- /dev/null +++ b/src/test/DUtilUnitTest/VarHelpers.cpp | |||
@@ -0,0 +1,147 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/VarHelpers.h b/src/test/DUtilUnitTest/VarHelpers.h new file mode 100644 index 00000000..9b781ce6 --- /dev/null +++ b/src/test/DUtilUnitTest/VarHelpers.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #pragma once | ||
2 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
3 | |||
4 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/VarUtilTest.cpp b/src/test/DUtilUnitTest/VarUtilTest.cpp new file mode 100644 index 00000000..206310f5 --- /dev/null +++ b/src/test/DUtilUnitTest/VarUtilTest.cpp | |||
@@ -0,0 +1,532 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | #undef GetTempPath | ||
5 | #undef GetEnvironmentVariable | ||
6 | |||
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 | } | ||
diff --git a/src/test/DUtilUnitTest/error.h b/src/test/DUtilUnitTest/error.h new file mode 100644 index 00000000..a52db56d --- /dev/null +++ b/src/test/DUtilUnitTest/error.h | |||
@@ -0,0 +1,8 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
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))); } | ||
diff --git a/src/test/DUtilUnitTest/packages.config b/src/test/DUtilUnitTest/packages.config new file mode 100644 index 00000000..17dcb258 --- /dev/null +++ b/src/test/DUtilUnitTest/packages.config | |||
@@ -0,0 +1,7 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | ||
2 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
3 | |||
4 | |||
5 | <packages> | ||
6 | <package id="xunit" version="1.9.1" targetFramework="net40" /> | ||
7 | </packages> | ||
diff --git a/src/test/DUtilUnitTest/precomp.cpp b/src/test/DUtilUnitTest/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/test/DUtilUnitTest/precomp.cpp | |||
@@ -0,0 +1,3 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h new file mode 100644 index 00000000..aa8f7de6 --- /dev/null +++ b/src/test/DUtilUnitTest/precomp.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #pragma once | ||
2 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
3 | |||
4 | |||
5 | #include <windows.h> | ||
6 | #include <strsafe.h> | ||
7 | #include <ShlObj.h> | ||
8 | |||
9 | // Include error.h before dutil.h | ||
10 | #include "error.h" | ||
11 | #include <dutil.h> | ||
12 | |||
13 | #include <dictutil.h> | ||
14 | #include <dirutil.h> | ||
15 | #include <fileutil.h> | ||
16 | #include <guidutil.h> | ||
17 | #include <iniutil.h> | ||
18 | #include <memutil.h> | ||
19 | #include <pathutil.h> | ||
20 | #include <strutil.h> | ||
21 | #include <monutil.h> | ||
22 | #include <regutil.h> | ||
23 | #include <uriutil.h> | ||
24 | #include <varutil.h> | ||
25 | #include <condutil.h> | ||
26 | |||
27 | #include "VarHelpers.h" | ||
28 | |||
29 | #pragma managed | ||
30 | #include <vcclr.h> | ||
diff --git a/src/test/DUtilUnitTest/resource.h b/src/test/DUtilUnitTest/resource.h new file mode 100644 index 00000000..bdf252f6 --- /dev/null +++ b/src/test/DUtilUnitTest/resource.h | |||
@@ -0,0 +1,2 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||