aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-05-26 17:33:15 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-05-31 13:20:44 -0500
commitfb4f8c7108f43d2341ba299424646c4963b21188 (patch)
tree7f3c907ac5406d2000056f6bcca41d7ae4d3a925
parent0f9931107ecf9e1f6714e6fd2cabc76d2ddb1153 (diff)
downloadwix-fb4f8c7108f43d2341ba299424646c4963b21188.tar.gz
wix-fb4f8c7108f43d2341ba299424646c4963b21188.tar.bz2
wix-fb4f8c7108f43d2341ba299424646c4963b21188.zip
Replace PathIsAbsolute with PathIsRooted and add PathIsFullyQualified.
-rw-r--r--src/burn/engine/cache.cpp2
-rw-r--r--src/burn/engine/logging.cpp2
-rw-r--r--src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h21
-rw-r--r--src/internal/WixBuildTools.TestSupport/XunitExtensions/SpecificReturnCodeException.cs19
-rw-r--r--src/internal/WixBuildTools.TestSupport/XunitExtensions/WixAssert.cs8
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h19
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp102
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp199
8 files changed, 350 insertions, 22 deletions
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index 5d81e1ba..2b7d6ede 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -563,7 +563,7 @@ extern "C" HRESULT CacheGetLocalSourcePaths(
563 fPreferSourcePathLocation = !pCache->fRunningFromCache || FAILED(hr); 563 fPreferSourcePathLocation = !pCache->fRunningFromCache || FAILED(hr);
564 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pCache->sczSourceProcessFolder, -1, sczLastSourceFolder, -1); 564 fTryLastFolder = SUCCEEDED(hr) && sczLastSourceFolder && *sczLastSourceFolder && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pCache->sczSourceProcessFolder, -1, sczLastSourceFolder, -1);
565 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1); 565 fTryRelativePath = CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzSourcePath, -1, wzRelativePath, -1);
566 fSourceIsAbsolute = PathIsAbsolute(wzSourcePath); 566 fSourceIsAbsolute = PathIsRooted(wzSourcePath);
567 567
568 // If the source path provided is a full path, try that first. 568 // If the source path provided is a full path, try that first.
569 if (fSourceIsAbsolute) 569 if (fSourceIsAbsolute)
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
index 1b2bec1f..8e89957b 100644
--- a/src/burn/engine/logging.cpp
+++ b/src/burn/engine/logging.cpp
@@ -140,7 +140,7 @@ extern "C" HRESULT LoggingOpen(
140 LPCWSTR wzPrefix = sczPrefixFormatted; 140 LPCWSTR wzPrefix = sczPrefixFormatted;
141 141
142 // Best effort to open default logging. 142 // Best effort to open default logging.
143 if (PathIsAbsolute(sczPrefixFormatted)) 143 if (PathIsRooted(sczPrefixFormatted))
144 { 144 {
145 hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder); 145 hr = PathGetDirectory(sczPrefixFormatted, &sczLoggingBaseFolder);
146 ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted); 146 ExitOnFailure(hr, "Failed to get parent directory from '%ls'.", sczPrefixFormatted);
diff --git a/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h b/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h
index 62ace4a9..b0206b14 100644
--- a/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h
+++ b/src/internal/WixBuildTools.TestSupport.Native/NativeAssert.h
@@ -64,6 +64,27 @@ namespace TestSupport {
64 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs); 64 WixAssert::Succeeded(hr, gcnew String(zFormat), formatArgs);
65 } 65 }
66 66
67 static void SpecificReturnCode(HRESULT hrExpected, HRESULT hr, LPCSTR zFormat, LPCSTR zArg, ... array<LPCSTR>^ zArgs)
68 {
69 array<Object^>^ formatArgs = gcnew array<Object^, 1>(zArgs->Length + 1);
70 formatArgs[0] = NativeAssert::LPSTRToString(zArg);
71 for (int i = 0; i < zArgs->Length; ++i)
72 {
73 formatArgs[i + 1] = NativeAssert::LPSTRToString(zArgs[i]);
74 }
75 WixAssert::SpecificReturnCode(hrExpected, hr, gcnew String(zFormat), formatArgs);
76 }
77
78 static void SpecificReturnCode(HRESULT hrExpected, HRESULT hr, LPCSTR zFormat, ... array<LPCWSTR>^ wzArgs)
79 {
80 array<Object^>^ formatArgs = gcnew array<Object^, 1>(wzArgs->Length);
81 for (int i = 0; i < wzArgs->Length; ++i)
82 {
83 formatArgs[i] = NativeAssert::LPWSTRToString(wzArgs[i]);
84 }
85 WixAssert::SpecificReturnCode(hrExpected, hr, gcnew String(zFormat), formatArgs);
86 }
87
67 static void ValidReturnCode(HRESULT hr, ... array<HRESULT>^ validReturnCodes) 88 static void ValidReturnCode(HRESULT hr, ... array<HRESULT>^ validReturnCodes)
68 { 89 {
69 Assert::Contains(hr, (IEnumerable<HRESULT>^)validReturnCodes); 90 Assert::Contains(hr, (IEnumerable<HRESULT>^)validReturnCodes);
diff --git a/src/internal/WixBuildTools.TestSupport/XunitExtensions/SpecificReturnCodeException.cs b/src/internal/WixBuildTools.TestSupport/XunitExtensions/SpecificReturnCodeException.cs
new file mode 100644
index 00000000..c66890f8
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/XunitExtensions/SpecificReturnCodeException.cs
@@ -0,0 +1,19 @@
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
3namespace WixBuildTools.TestSupport
4{
5 using System;
6 using Xunit.Sdk;
7
8 public class SpecificReturnCodeException : XunitException
9 {
10 public SpecificReturnCodeException(int hr, string userMessage)
11 : base(String.Format("WixAssert.SpecificReturnCode() Failure\r\n" +
12 "HRESULT: 0x{0:X8}\r\n" +
13 "Message: {1}",
14 hr, userMessage))
15 {
16 this.HResult = hr;
17 }
18 }
19}
diff --git a/src/internal/WixBuildTools.TestSupport/XunitExtensions/WixAssert.cs b/src/internal/WixBuildTools.TestSupport/XunitExtensions/WixAssert.cs
index 10156547..1ede55b3 100644
--- a/src/internal/WixBuildTools.TestSupport/XunitExtensions/WixAssert.cs
+++ b/src/internal/WixBuildTools.TestSupport/XunitExtensions/WixAssert.cs
@@ -52,6 +52,14 @@ namespace WixBuildTools.TestSupport
52 throw new SkipTestException(message); 52 throw new SkipTestException(message);
53 } 53 }
54 54
55 public static void SpecificReturnCode(int hrExpected, int hr, string format, params object[] formatArgs)
56 {
57 if (hrExpected != hr)
58 {
59 throw new SpecificReturnCodeException(hr, String.Format(format, formatArgs));
60 }
61 }
62
55 public static void Succeeded(int hr, string format, params object[] formatArgs) 63 public static void Succeeded(int hr, string format, params object[] formatArgs)
56 { 64 {
57 if (0 > hr) 65 if (0 > hr)
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
index 44d36568..602b4a80 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -166,10 +166,23 @@ DAPI_(HRESULT) PathGetKnownFolder(
166 ); 166 );
167 167
168/******************************************************************* 168/*******************************************************************
169 PathIsAbsolute - returns true if the path is absolute; false 169 PathIsFullyQualified - returns true if the path is fully qualified; false otherwise.
170 otherwise. 170 Note that some rooted paths like C:dir are not fully qualified.
171 For example, these are all fully qualified: C:\dir, \\server\share, \\?\C:\dir.
172 For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir.
171*******************************************************************/ 173*******************************************************************/
172DAPI_(BOOL) PathIsAbsolute( 174DAPI_(BOOL) PathIsFullyQualified(
175 __in_z LPCWSTR wzPath,
176 __out_opt BOOL* pfHasLongPathPrefix
177 );
178
179/*******************************************************************
180 PathIsRooted - returns true if the path is rooted; false otherwise.
181 Note that some rooted paths like C:dir are not fully qualified.
182 For example, these are all rooted: C:\dir, C:dir, C:, \dir, \\server\share, \\?\C:\dir.
183 For example, these are not rooted: dir, dir\subdir.
184*******************************************************************/
185DAPI_(BOOL) PathIsRooted(
173 __in_z LPCWSTR wzPath 186 __in_z LPCWSTR wzPath
174 ); 187 );
175 188
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
index 9823779b..314eab85 100644
--- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -20,6 +20,10 @@
20 20
21#define PATH_GOOD_ENOUGH 64 21#define PATH_GOOD_ENOUGH 64
22 22
23static BOOL IsValidDriveChar(
24 __in WCHAR wc
25 );
26
23 27
24DAPI_(LPWSTR) PathFile( 28DAPI_(LPWSTR) PathFile(
25 __in_z LPCWSTR wzPath 29 __in_z LPCWSTR wzPath
@@ -272,29 +276,30 @@ DAPI_(HRESULT) PathPrefix(
272 276
273 HRESULT hr = S_OK; 277 HRESULT hr = S_OK;
274 LPWSTR wzFullPath = *psczFullPath; 278 LPWSTR wzFullPath = *psczFullPath;
279 BOOL fFullyQualified = FALSE;
280 BOOL fHasPrefix = FALSE;
275 SIZE_T cbFullPath = 0; 281 SIZE_T cbFullPath = 0;
276 282
277 if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || 283 fFullyQualified = PathIsFullyQualified(wzFullPath, &fHasPrefix);
278 (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && 284 if (fHasPrefix)
279 L':' == wzFullPath[1] && 285 {
280 L'\\' == wzFullPath[2]) // normal path 286 ExitFunction();
287 }
288
289 if (fFullyQualified && L':' == wzFullPath[1]) // normal path
281 { 290 {
282 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); 291 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
283 PathExitOnFailure(hr, "Failed to add prefix to file path."); 292 PathExitOnFailure(hr, "Failed to add prefix to file path.");
284 } 293 }
285 else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC 294 else if (fFullyQualified && L'\\' == wzFullPath[1]) // UNC
286 { 295 {
287 // ensure that we're not already prefixed 296 hr = StrSize(*psczFullPath, &cbFullPath);
288 if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) 297 PathExitOnFailure(hr, "Failed to get size of full path.");
289 {
290 hr = StrSize(*psczFullPath, &cbFullPath);
291 PathExitOnFailure(hr, "Failed to get size of full path.");
292 298
293 memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); 299 memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR));
294 300
295 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); 301 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
296 PathExitOnFailure(hr, "Failed to add prefix to UNC path."); 302 PathExitOnFailure(hr, "Failed to add prefix to UNC path.");
297 }
298 } 303 }
299 else 304 else
300 { 305 {
@@ -814,11 +819,66 @@ LExit:
814} 819}
815 820
816 821
817DAPI_(BOOL) PathIsAbsolute( 822DAPI_(BOOL) PathIsFullyQualified(
823 __in_z LPCWSTR wzPath,
824 __out_opt BOOL* pfHasLongPathPrefix
825 )
826{
827 BOOL fFullyQualified = FALSE;
828 BOOL fHasLongPathPrefix = FALSE;
829
830 if (!wzPath || !wzPath[0] || !wzPath[1])
831 {
832 // There is no way to specify a fully qualified path with one character (or less).
833 ExitFunction();
834 }
835
836 if (L'\\' != wzPath[0])
837 {
838 // The only way to specify a fully qualified path that doesn't begin with a slash
839 // is the drive, colon, slash format (C:\).
840 if (IsValidDriveChar(wzPath[0]) &&
841 L':' == wzPath[1] &&
842 L'\\' == wzPath[2])
843 {
844 fFullyQualified = TRUE;
845 }
846
847 ExitFunction();
848 }
849
850 // Non-drive fully qualified paths must start with \\ or \?.
851 // \??\ is an archaic form of \\?\.
852 if (L'?' != wzPath[1] && L'\\' != wzPath[1])
853 {
854 ExitFunction();
855 }
856
857 fFullyQualified = TRUE;
858
859 if (L'?' == wzPath[2] && L'\\' == wzPath[3])
860 {
861 fHasLongPathPrefix = TRUE;
862 }
863
864
865LExit:
866 if (pfHasLongPathPrefix)
867 {
868 *pfHasLongPathPrefix = fHasLongPathPrefix;
869 }
870
871 return fFullyQualified;
872}
873
874
875DAPI_(BOOL) PathIsRooted(
818 __in_z LPCWSTR wzPath 876 __in_z LPCWSTR wzPath
819 ) 877 )
820{ 878{
821 return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); 879 return wzPath &&
880 (wzPath[0] == L'\\' ||
881 IsValidDriveChar(wzPath[0]) && wzPath[1] == L':');
822} 882}
823 883
824 884
@@ -847,7 +907,7 @@ DAPI_(HRESULT) PathConcatCch(
847 hr = StrAllocString(psczCombined, wzPath1, cchPath1); 907 hr = StrAllocString(psczCombined, wzPath1, cchPath1);
848 PathExitOnFailure(hr, "Failed to copy just path1 to output."); 908 PathExitOnFailure(hr, "Failed to copy just path1 to output.");
849 } 909 }
850 else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) 910 else if (!wzPath1 || !*wzPath1 || PathIsRooted(wzPath2))
851 { 911 {
852 hr = StrAllocString(psczCombined, wzPath2, cchPath2); 912 hr = StrAllocString(psczCombined, wzPath2, cchPath2);
853 PathExitOnFailure(hr, "Failed to copy just path2 to output."); 913 PathExitOnFailure(hr, "Failed to copy just path2 to output.");
@@ -1002,3 +1062,11 @@ LExit:
1002 1062
1003 return hr; 1063 return hr;
1004} 1064}
1065
1066static BOOL IsValidDriveChar(
1067 __in WCHAR wc
1068 )
1069{
1070 return L'a' <= wc && L'z' >= wc ||
1071 L'A' <= wc && L'Z' >= wc;
1072}
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index 6a05a45c..65856514 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -109,5 +109,204 @@ namespace DutilTests
109 ReleaseStrArray(rgsczPaths, cPaths); 109 ReleaseStrArray(rgsczPaths, cPaths);
110 } 110 }
111 } 111 }
112
113 [Fact]
114 void PathPrefixTest()
115 {
116 HRESULT hr = S_OK;
117 LPWSTR sczPath = NULL;
118 LPCWSTR rgwzPaths[12] =
119 {
120 L"\\\\", L"\\\\?\\UNC\\",
121 L"C:\\\\foo2", L"\\\\?\\C:\\\\foo2",
122 L"\\\\a\\b\\", L"\\\\?\\UNC\\a\\b\\",
123 L"\\\\?\\UNC\\test\\unc\\path\\to\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
124 L"\\\\?\\C:\\foo\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
125 L"\\??\\C:\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt",
126 };
127
128 try
129 {
130 for (DWORD i = 0; i < countof(rgwzPaths) / 2; i += 2)
131 {
132 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
133 NativeAssert::Succeeded(hr, "Failed to copy string");
134
135 hr = PathPrefix(&sczPath);
136 NativeAssert::Succeeded(hr, "PathPrefix: {0}", rgwzPaths[i]);
137 NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
138 }
139 }
140 finally
141 {
142 ReleaseStr(sczPath);
143 }
144 }
145
146 [Fact]
147 void PathPrefixFailureTest()
148 {
149 HRESULT hr = S_OK;
150 LPWSTR sczPath = NULL;
151 LPCWSTR rgwzPaths[8] =
152 {
153 L"\\",
154 L"C:",
155 L"C:foo.txt",
156 L"",
157 L"\\?",
158 L"\\dir",
159 L"dir",
160 L"dir\\subdir",
161 };
162
163 try
164 {
165 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
166 {
167 hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
168 NativeAssert::Succeeded(hr, "Failed to copy string");
169
170 hr = PathPrefix(&sczPath);
171 NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathPrefix: {0}, {1}", rgwzPaths[i], sczPath);
172 }
173 }
174 finally
175 {
176 ReleaseStr(sczPath);
177 }
178 }
179
180 [Fact]
181 void PathIsRootedAndFullyQualifiedTest()
182 {
183 LPCWSTR rgwzPaths[15] =
184 {
185 L"\\\\",
186 L"\\\\\\",
187 L"C:\\",
188 L"C:\\\\",
189 L"C:\\foo1",
190 L"C:\\\\foo2",
191 L"\\\\test\\unc\\path\\to\\something",
192 L"\\\\a\\b\\c\\d\\e",
193 L"\\\\a\\b\\",
194 L"\\\\a\\b",
195 L"\\\\test\\unc",
196 L"\\\\Server",
197 L"\\\\Server\\Foo.txt",
198 L"\\\\Server\\Share\\Foo.txt",
199 L"\\\\Server\\Share\\Test\\Foo.txt",
200 };
201
202 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
203 {
204 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE);
205 ValidateRootedPath(rgwzPaths[i], TRUE);
206 }
207 }
208
209 [Fact]
210 void PathIsRootedAndFullyQualifiedWithPrefixTest()
211 {
212 LPCWSTR rgwzPaths[6] =
213 {
214 L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
215 L"\\\\?\\UNC\\test\\unc",
216 L"\\\\?\\UNC\\a\\b1",
217 L"\\\\?\\UNC\\a\\b2\\",
218 L"\\\\?\\C:\\foo\\bar.txt",
219 L"\\??\\C:\\foo\\bar.txt",
220 };
221
222 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
223 {
224 ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE);
225 ValidateRootedPath(rgwzPaths[i], TRUE);
226 }
227 }
228
229 [Fact]
230 void PathIsRootedButNotFullyQualifiedTest()
231 {
232 LPCWSTR rgwzPaths[7] =
233 {
234 L"\\",
235 L"a:",
236 L"A:",
237 L"z:",
238 L"Z:",
239 L"C:foo.txt",
240 L"\\dir",
241 };
242
243 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
244 {
245 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
246 ValidateRootedPath(rgwzPaths[i], TRUE);
247 }
248 }
249
250 [Fact]
251 void PathIsNotRootedAndNotFullyQualifiedTest()
252 {
253 LPCWSTR rgwzPaths[9] =
254 {
255 NULL,
256 L"",
257 L"dir",
258 L"dir\\subdir",
259 L"@:\\foo", // 064 = @ 065 = A
260 L"[:\\\\", // 091 = [ 090 = Z
261 L"`:\\foo ", // 096 = ` 097 = a
262 L"{:\\\\", // 123 = { 122 = z
263 L"[:",
264 };
265
266 for (DWORD i = 0; i < countof(rgwzPaths); ++i)
267 {
268 ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
269 ValidateRootedPath(rgwzPaths[i], FALSE);
270 }
271 }
272
273 void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected, BOOL fExpectedHasPrefix)
274 {
275 BOOL fHasLongPathPrefix = FALSE;
276 BOOL fRooted = PathIsFullyQualified(wzPath, &fHasLongPathPrefix);
277 String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath));
278 if (fExpected)
279 {
280 Assert::True(fRooted, message);
281 }
282 else
283 {
284 Assert::False(fRooted, message);
285 }
286
287 message = String::Format("HasLongPathPrefix: {0}", gcnew String(wzPath));
288 if (fExpectedHasPrefix)
289 {
290 Assert::True(fHasLongPathPrefix, message);
291 }
292 else
293 {
294 Assert::False(fHasLongPathPrefix, message);
295 }
296 }
297
298 void ValidateRootedPath(LPCWSTR wzPath, BOOL fExpected)
299 {
300 BOOL fRooted = PathIsRooted(wzPath);
301 String^ message = String::Format("IsRooted: {0}", gcnew String(wzPath));
302 if (fExpected)
303 {
304 Assert::True(fRooted, message);
305 }
306 else
307 {
308 Assert::False(fRooted, message);
309 }
310 }
112 }; 311 };
113} 312}