aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2020-08-29 21:29:28 -0500
committerSean Hall <r.sean.hall@gmail.com>2020-10-24 20:03:38 -0500
commitd1d31466bb9f2e887a277807d60378afef9cc57d (patch)
tree90b0185ce83f4ec5d986205aff2ce2487d3260de
parent281ad838c5001f988aeea06a6f06ce2cc6c0991d (diff)
downloadwix-d1d31466bb9f2e887a277807d60378afef9cc57d.tar.gz
wix-d1d31466bb9f2e887a277807d60378afef9cc57d.tar.bz2
wix-d1d31466bb9f2e887a277807d60378afef9cc57d.zip
WIXFEAT:6210 Parse and compare bundle versions kind of like SemVer.
-rw-r--r--src/dutil/dutil.vcxproj2
-rw-r--r--src/dutil/dutil.vcxproj.filters6
-rw-r--r--src/dutil/inc/dutilsources.h1
-rw-r--r--src/dutil/inc/verutil.h93
-rw-r--r--src/dutil/precomp.h1
-rw-r--r--src/dutil/verutil.cpp631
-rw-r--r--src/test/DUtilUnitTest/DUtilUnitTest.vcxproj1
-rw-r--r--src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters3
-rw-r--r--src/test/DUtilUnitTest/VerUtilTests.cpp891
-rw-r--r--src/test/DUtilUnitTest/precomp.h1
10 files changed, 1630 insertions, 0 deletions
diff --git a/src/dutil/dutil.vcxproj b/src/dutil/dutil.vcxproj
index e9bbb98b..017f7a6f 100644
--- a/src/dutil/dutil.vcxproj
+++ b/src/dutil/dutil.vcxproj
@@ -111,6 +111,7 @@
111 <ClCompile Include="uncutil.cpp" /> 111 <ClCompile Include="uncutil.cpp" />
112 <ClCompile Include="uriutil.cpp" /> 112 <ClCompile Include="uriutil.cpp" />
113 <ClCompile Include="userutil.cpp" /> 113 <ClCompile Include="userutil.cpp" />
114 <ClCompile Include="verutil.cpp" />
114 <ClCompile Include="wiutil.cpp" /> 115 <ClCompile Include="wiutil.cpp" />
115 <ClCompile Include="wuautil.cpp" /> 116 <ClCompile Include="wuautil.cpp" />
116 <ClCompile Include="xmlutil.cpp" /> 117 <ClCompile Include="xmlutil.cpp" />
@@ -167,6 +168,7 @@
167 <ClInclude Include="inc\timeutil.h" /> 168 <ClInclude Include="inc\timeutil.h" />
168 <ClInclude Include="inc\uriutil.h" /> 169 <ClInclude Include="inc\uriutil.h" />
169 <ClInclude Include="inc\userutil.h" /> 170 <ClInclude Include="inc\userutil.h" />
171 <ClInclude Include="inc\verutil.h" />
170 <ClInclude Include="inc\wiutil.h" /> 172 <ClInclude Include="inc\wiutil.h" />
171 <ClInclude Include="inc\wuautil.h" /> 173 <ClInclude Include="inc\wuautil.h" />
172 <ClInclude Include="inc\xmlutil.h" /> 174 <ClInclude Include="inc\xmlutil.h" />
diff --git a/src/dutil/dutil.vcxproj.filters b/src/dutil/dutil.vcxproj.filters
index 01dd6661..b93d166b 100644
--- a/src/dutil/dutil.vcxproj.filters
+++ b/src/dutil/dutil.vcxproj.filters
@@ -159,6 +159,9 @@
159 <ClCompile Include="userutil.cpp"> 159 <ClCompile Include="userutil.cpp">
160 <Filter>Source Files</Filter> 160 <Filter>Source Files</Filter>
161 </ClCompile> 161 </ClCompile>
162 <ClCompile Include="verutil.cpp">
163 <Filter>Source Files</Filter>
164 </ClCompile>
162 <ClCompile Include="wiutil.cpp"> 165 <ClCompile Include="wiutil.cpp">
163 <Filter>Source Files</Filter> 166 <Filter>Source Files</Filter>
164 </ClCompile> 167 </ClCompile>
@@ -329,6 +332,9 @@
329 <ClInclude Include="inc\userutil.h"> 332 <ClInclude Include="inc\userutil.h">
330 <Filter>Header Files</Filter> 333 <Filter>Header Files</Filter>
331 </ClInclude> 334 </ClInclude>
335 <ClInclude Include="inc\verutil.h">
336 <Filter>Header Files</Filter>
337 </ClInclude>
332 <ClInclude Include="inc\wiutil.h"> 338 <ClInclude Include="inc\wiutil.h">
333 <Filter>Header Files</Filter> 339 <Filter>Header Files</Filter>
334 </ClInclude> 340 </ClInclude>
diff --git a/src/dutil/inc/dutilsources.h b/src/dutil/inc/dutilsources.h
index b03013ca..7d512cb3 100644
--- a/src/dutil/inc/dutilsources.h
+++ b/src/dutil/inc/dutilsources.h
@@ -60,6 +60,7 @@ typedef enum DUTIL_SOURCE
60 DUTIL_SOURCE_WIUTIL, 60 DUTIL_SOURCE_WIUTIL,
61 DUTIL_SOURCE_WUAUTIL, 61 DUTIL_SOURCE_WUAUTIL,
62 DUTIL_SOURCE_XMLUTIL, 62 DUTIL_SOURCE_XMLUTIL,
63 DUTIL_SOURCE_VERUTIL,
63 64
64 DUTIL_SOURCE_EXTERNAL = 256, 65 DUTIL_SOURCE_EXTERNAL = 256,
65} DUTIL_SOURCE; 66} DUTIL_SOURCE;
diff --git a/src/dutil/inc/verutil.h b/src/dutil/inc/verutil.h
new file mode 100644
index 00000000..d3715049
--- /dev/null
+++ b/src/dutil/inc/verutil.h
@@ -0,0 +1,93 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; }
10
11typedef struct _VERUTIL_VERSION_RELEASE_LABEL
12{
13 BOOL fNumeric;
14 DWORD dwValue;
15 DWORD_PTR cchLabelOffset;
16 int cchLabel;
17} VERUTIL_VERSION_RELEASE_LABEL;
18
19typedef struct _VERUTIL_VERSION
20{
21 LPWSTR sczVersion;
22 DWORD dwMajor;
23 DWORD dwMinor;
24 DWORD dwPatch;
25 DWORD dwRevision;
26 DWORD cReleaseLabels;
27 VERUTIL_VERSION_RELEASE_LABEL* rgReleaseLabels;
28 DWORD_PTR cchMetadataOffset;
29 BOOL fInvalid;
30} VERUTIL_VERSION;
31
32/*******************************************************************
33 VerCompareParsedVersions - compares the Verutil versions.
34
35*******************************************************************/
36HRESULT DAPI VerCompareParsedVersions(
37 __in VERUTIL_VERSION* pVersion1,
38 __in VERUTIL_VERSION* pVersion2,
39 __out int* pnResult
40 );
41
42/*******************************************************************
43 VerCompareStringVersions - parses the strings with VerParseVersion and then
44 compares the Verutil versions with VerCompareParsedVersions.
45
46*******************************************************************/
47HRESULT DAPI VerCompareStringVersions(
48 __in_z LPCWSTR wzVersion1,
49 __in_z LPCWSTR wzVersion2,
50 __in BOOL fStrict,
51 __out int* pnResult
52 );
53
54/********************************************************************
55 VerCopyVersion - copies the given Verutil version.
56
57*******************************************************************/
58HRESULT DAPI VerCopyVersion(
59 __in VERUTIL_VERSION* pSource,
60 __out VERUTIL_VERSION** ppVersion
61 );
62
63/********************************************************************
64 VerFreeVersion - frees any memory associated with a Verutil version.
65
66*******************************************************************/
67void DAPI VerFreeVersion(
68 __in VERUTIL_VERSION* pVersion
69 );
70
71/*******************************************************************
72 VerParseVersion - parses the string into a Verutil version.
73
74*******************************************************************/
75HRESULT DAPI VerParseVersion(
76 __in_z LPCWSTR wzVersion,
77 __in DWORD cchVersion,
78 __in BOOL fStrict,
79 __out VERUTIL_VERSION** ppVersion
80 );
81
82/*******************************************************************
83 VerParseVersion - parses the QWORD into a Verutil version.
84
85*******************************************************************/
86HRESULT DAPI VerVersionFromQword(
87 __in DWORD64 qwVersion,
88 __out VERUTIL_VERSION** ppVersion
89 );
90
91#ifdef __cplusplus
92}
93#endif
diff --git a/src/dutil/precomp.h b/src/dutil/precomp.h
index 7fdc83ae..be58860c 100644
--- a/src/dutil/precomp.h
+++ b/src/dutil/precomp.h
@@ -89,6 +89,7 @@
89#include "uncutil.h" 89#include "uncutil.h"
90#include "uriutil.h" 90#include "uriutil.h"
91#include "userutil.h" 91#include "userutil.h"
92#include "verutil.h"
92#include "wiutil.h" 93#include "wiutil.h"
93#include "wuautil.h" 94#include "wuautil.h"
94#include <comutil.h> // This header is needed for msxml2.h to compile correctly 95#include <comutil.h> // This header is needed for msxml2.h to compile correctly
diff --git a/src/dutil/verutil.cpp b/src/dutil/verutil.cpp
new file mode 100644
index 00000000..f362f413
--- /dev/null
+++ b/src/dutil/verutil.cpp
@@ -0,0 +1,631 @@
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// Exit macros
6#define VerExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
7#define VerExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
8#define VerExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
9#define VerExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
10#define VerExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
11#define VerExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_VERUTIL, x, s, __VA_ARGS__)
12#define VerExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
13#define VerExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
14#define VerExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_VERUTIL, p, x, e, s, __VA_ARGS__)
15#define VerExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_VERUTIL, p, x, s, __VA_ARGS__)
16#define VerExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_VERUTIL, e, x, s, __VA_ARGS__)
17
18// constants
19const DWORD GROW_RELEASE_LABELS = 3;
20
21// Forward declarations.
22static int CompareDword(
23 __in const DWORD& dw1,
24 __in const DWORD& dw2
25 );
26static HRESULT CompareReleaseLabel(
27 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
28 __in LPCWSTR wzVersion1,
29 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
30 __in LPCWSTR wzVersion2,
31 __out int* pnResult
32 );
33static HRESULT CompareVersionSubstring(
34 __in LPCWSTR wzString1,
35 __in int cchCount1,
36 __in LPCWSTR wzString2,
37 __in int cchCount2,
38 __out int* pnResult
39 );
40
41
42DAPI_(HRESULT) VerCompareParsedVersions(
43 __in VERUTIL_VERSION* pVersion1,
44 __in VERUTIL_VERSION* pVersion2,
45 __out int* pnResult
46 )
47{
48 HRESULT hr = S_OK;
49 int nResult = 0;
50 DWORD cMaxReleaseLabels = 0;
51 BOOL fCompareMetadata = FALSE;
52
53 if (!pVersion1 || !pVersion1->sczVersion ||
54 !pVersion2 || !pVersion2->sczVersion)
55 {
56 ExitFunction1(hr = E_INVALIDARG);
57 }
58
59 if (pVersion1 == pVersion2)
60 {
61 ExitFunction1(nResult = 0);
62 }
63
64 nResult = CompareDword(pVersion1->dwMajor, pVersion2->dwMajor);
65 if (0 != nResult)
66 {
67 ExitFunction();
68 }
69
70 nResult = CompareDword(pVersion1->dwMinor, pVersion2->dwMinor);
71 if (0 != nResult)
72 {
73 ExitFunction();
74 }
75
76 nResult = CompareDword(pVersion1->dwPatch, pVersion2->dwPatch);
77 if (0 != nResult)
78 {
79 ExitFunction();
80 }
81
82 nResult = CompareDword(pVersion1->dwRevision, pVersion2->dwRevision);
83 if (0 != nResult)
84 {
85 ExitFunction();
86 }
87
88 if (pVersion1->fInvalid)
89 {
90 if (!pVersion2->fInvalid)
91 {
92 ExitFunction1(nResult = -1);
93 }
94 else
95 {
96 fCompareMetadata = TRUE;
97 }
98 }
99 else if (pVersion2->fInvalid)
100 {
101 ExitFunction1(nResult = 1);
102 }
103
104 if (pVersion1->cReleaseLabels)
105 {
106 if (pVersion2->cReleaseLabels)
107 {
108 cMaxReleaseLabels = max(pVersion1->cReleaseLabels, pVersion2->cReleaseLabels);
109 }
110 else
111 {
112 ExitFunction1(nResult = -1);
113 }
114 }
115 else if (pVersion2->cReleaseLabels)
116 {
117 ExitFunction1(nResult = 1);
118 }
119
120 if (cMaxReleaseLabels)
121 {
122 for (DWORD i = 0; i < cMaxReleaseLabels; ++i)
123 {
124 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel1 = pVersion1->cReleaseLabels > i ? pVersion1->rgReleaseLabels + i : NULL;
125 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel2 = pVersion2->cReleaseLabels > i ? pVersion2->rgReleaseLabels + i : NULL;
126
127 hr = CompareReleaseLabel(pReleaseLabel1, pVersion1->sczVersion, pReleaseLabel2, pVersion2->sczVersion, &nResult);
128 if (FAILED(hr) || 0 != nResult)
129 {
130 ExitFunction();
131 }
132 }
133 }
134
135 if (fCompareMetadata)
136 {
137 hr = CompareVersionSubstring(pVersion1->sczVersion + pVersion1->cchMetadataOffset, -1, pVersion2->sczVersion + pVersion2->cchMetadataOffset, -1, &nResult);
138 }
139
140LExit:
141 *pnResult = nResult;
142 return hr;
143}
144
145DAPI_(HRESULT) VerCompareStringVersions(
146 __in_z LPCWSTR wzVersion1,
147 __in_z LPCWSTR wzVersion2,
148 __in BOOL fStrict,
149 __out int* pnResult
150 )
151{
152 HRESULT hr = S_OK;
153 VERUTIL_VERSION* pVersion1 = NULL;
154 VERUTIL_VERSION* pVersion2 = NULL;
155 int nResult = 0;
156
157 hr = VerParseVersion(wzVersion1, 0, fStrict, &pVersion1);
158 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion1);
159
160 hr = VerParseVersion(wzVersion2, 0, fStrict, &pVersion2);
161 VerExitOnFailure(hr, "Failed to parse Verutil version '%ls'", wzVersion2);
162
163 hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
164 VerExitOnFailure(hr, "Failed to compare parsed Verutil versions '%ls' and '%ls'.", wzVersion1, wzVersion2);
165
166LExit:
167 *pnResult = nResult;
168
169 ReleaseVerutilVersion(pVersion1);
170 ReleaseVerutilVersion(pVersion2);
171
172 return hr;
173}
174
175DAPI_(HRESULT) VerCopyVersion(
176 __in VERUTIL_VERSION* pSource,
177 __out VERUTIL_VERSION** ppVersion
178 )
179{
180 HRESULT hr = S_OK;
181 VERUTIL_VERSION* pCopy = NULL;
182
183 pCopy = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
184 VerExitOnNull(pCopy, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version copy.");
185
186 hr = StrAllocString(&pCopy->sczVersion, pSource->sczVersion, 0);
187 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", pSource->sczVersion);
188
189 pCopy->dwMajor = pSource->dwMajor;
190 pCopy->dwMinor = pSource->dwMinor;
191 pCopy->dwPatch = pSource->dwPatch;
192 pCopy->dwRevision = pSource->dwRevision;
193
194 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pCopy->rgReleaseLabels), 0, sizeof(VERUTIL_VERSION_RELEASE_LABEL), pSource->cReleaseLabels);
195 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels copies.");
196
197 pCopy->cReleaseLabels = pSource->cReleaseLabels;
198
199 for (DWORD i = 0; i < pCopy->cReleaseLabels; ++i)
200 {
201 VERUTIL_VERSION_RELEASE_LABEL* pSourceLabel = pSource->rgReleaseLabels + i;
202 VERUTIL_VERSION_RELEASE_LABEL* pCopyLabel = pCopy->rgReleaseLabels + i;
203
204 pCopyLabel->cchLabelOffset = pSourceLabel->cchLabelOffset;
205 pCopyLabel->cchLabel = pSourceLabel->cchLabel;
206 pCopyLabel->fNumeric = pSourceLabel->fNumeric;
207 pCopyLabel->dwValue = pSourceLabel->dwValue;
208 }
209
210 pCopy->cchMetadataOffset = pSource->cchMetadataOffset;
211 pCopy->fInvalid = pSource->fInvalid;
212
213 *ppVersion = pCopy;
214 pCopy = NULL;
215
216LExit:
217 ReleaseVerutilVersion(pCopy);
218
219 return hr;
220}
221
222DAPI_(void) VerFreeVersion(
223 __in VERUTIL_VERSION* pVersion
224 )
225{
226 if (pVersion)
227 {
228 ReleaseStr(pVersion->sczVersion);
229 ReleaseMem(pVersion->rgReleaseLabels);
230 ReleaseMem(pVersion);
231 }
232}
233
234DAPI_(HRESULT) VerParseVersion(
235 __in_z LPCWSTR wzVersion,
236 __in DWORD cchVersion,
237 __in BOOL fStrict,
238 __out VERUTIL_VERSION** ppVersion
239 )
240{
241 HRESULT hr = S_OK;
242 VERUTIL_VERSION* pVersion = NULL;
243 LPCWSTR wzEnd = NULL;
244 LPCWSTR wzPartBegin = NULL;
245 LPCWSTR wzPartEnd = NULL;
246 BOOL fInvalid = FALSE;
247 BOOL fLastPart = FALSE;
248 BOOL fTrailingDot = FALSE;
249 BOOL fParsedVersionNumber = FALSE;
250 BOOL fExpectedReleaseLabels = FALSE;
251 DWORD iPart = 0;
252
253 if (!wzVersion || !ppVersion)
254 {
255 ExitFunction1(hr = E_INVALIDARG);
256 }
257
258 // Get string length if not provided.
259 if (0 == cchVersion)
260 {
261 cchVersion = lstrlenW(wzVersion);
262 }
263
264 if (L'v' == *wzVersion || L'V' == *wzVersion)
265 {
266 ++wzVersion;
267 --cchVersion;
268 }
269
270 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
271 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version '%ls'.", wzVersion);
272
273 hr = StrAllocString(&pVersion->sczVersion, wzVersion, cchVersion);
274 VerExitOnFailure(hr, "Failed to copy Verutil version string '%ls'.", wzVersion);
275
276 wzVersion = wzPartBegin = wzPartEnd = pVersion->sczVersion;
277
278 // Save end pointer.
279 wzEnd = wzVersion + cchVersion;
280
281 // Parse version number
282 while (wzPartBegin < wzEnd)
283 {
284 fTrailingDot = FALSE;
285
286 // Find end of part.
287 for (;;)
288 {
289 if (wzPartEnd >= wzEnd)
290 {
291 fLastPart = TRUE;
292 break;
293 }
294
295 switch (*wzPartEnd)
296 {
297 case L'0':
298 case L'1':
299 case L'2':
300 case L'3':
301 case L'4':
302 case L'5':
303 case L'6':
304 case L'7':
305 case L'8':
306 case L'9':
307 ++wzPartEnd;
308 continue;
309 case L'.':
310 fTrailingDot = TRUE;
311 break;
312 case L'-':
313 case L'+':
314 fLastPart = TRUE;
315 break;
316 default:
317 fInvalid = TRUE;
318 break;
319 }
320
321 break;
322 }
323
324 if (wzPartBegin == wzPartEnd)
325 {
326 fInvalid = TRUE;
327 }
328
329 if (fInvalid)
330 {
331 break;
332 }
333
334 DWORD cchPart = 0;
335 hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart);
336 if (FAILED(hr))
337 {
338 fInvalid = TRUE;
339 break;
340 }
341
342 // Parse version part.
343 UINT uPart = 0;
344 hr = StrStringToUInt32(wzPartBegin, cchPart, &uPart);
345 if (FAILED(hr))
346 {
347 fInvalid = TRUE;
348 break;
349 }
350
351 switch (iPart)
352 {
353 case 0:
354 pVersion->dwMajor = uPart;
355 break;
356 case 1:
357 pVersion->dwMinor = uPart;
358 break;
359 case 2:
360 pVersion->dwPatch = uPart;
361 break;
362 case 3:
363 pVersion->dwRevision = uPart;
364 break;
365 }
366
367 if (fTrailingDot)
368 {
369 ++wzPartEnd;
370 }
371 wzPartBegin = wzPartEnd;
372 ++iPart;
373
374 if (4 <= iPart || fLastPart)
375 {
376 fParsedVersionNumber = TRUE;
377 break;
378 }
379 }
380
381 fInvalid |= !fParsedVersionNumber || fTrailingDot;
382
383 if (!fInvalid && wzPartBegin < wzEnd && *wzPartBegin == L'-')
384 {
385 wzPartBegin = wzPartEnd = wzPartBegin + 1;
386 fExpectedReleaseLabels = TRUE;
387 fLastPart = FALSE;
388 }
389
390 while (fExpectedReleaseLabels && wzPartBegin < wzEnd)
391 {
392 fTrailingDot = FALSE;
393
394 // Find end of part.
395 for (;;)
396 {
397 if (wzPartEnd >= wzEnd)
398 {
399 fLastPart = TRUE;
400 break;
401 }
402
403 if (*wzPartEnd >= L'0' && *wzPartEnd <= L'9' ||
404 *wzPartEnd >= L'A' && *wzPartEnd <= L'Z' ||
405 *wzPartEnd >= L'a' && *wzPartEnd <= L'z' ||
406 *wzPartEnd == L'-')
407 {
408 ++wzPartEnd;
409 continue;
410 }
411 else if (*wzPartEnd == L'+')
412 {
413 fLastPart = TRUE;
414 }
415 else if (*wzPartEnd == L'.')
416 {
417 fTrailingDot = TRUE;
418 }
419 else
420 {
421 fInvalid = TRUE;
422 }
423
424 break;
425 }
426
427 if (wzPartBegin == wzPartEnd)
428 {
429 fInvalid = TRUE;
430 }
431
432 if (fInvalid)
433 {
434 break;
435 }
436
437 int cchLabel = 0;
438 hr = ::PtrdiffTToInt32(wzPartEnd - wzPartBegin, &cchLabel);
439 if (FAILED(hr) || 0 > cchLabel)
440 {
441 fInvalid = TRUE;
442 break;
443 }
444
445 hr = MemReAllocArray(reinterpret_cast<LPVOID*>(&pVersion->rgReleaseLabels), pVersion->cReleaseLabels, sizeof(VERUTIL_VERSION_RELEASE_LABEL), GROW_RELEASE_LABELS - (pVersion->cReleaseLabels % GROW_RELEASE_LABELS));
446 VerExitOnFailure(hr, "Failed to allocate memory for Verutil version release labels '%ls'", wzVersion);
447
448 VERUTIL_VERSION_RELEASE_LABEL* pReleaseLabel = pVersion->rgReleaseLabels + pVersion->cReleaseLabels;
449 ++pVersion->cReleaseLabels;
450
451 // Try to parse as number.
452 UINT uLabel = 0;
453 hr = StrStringToUInt32(wzPartBegin, cchLabel, &uLabel);
454 if (SUCCEEDED(hr))
455 {
456 pReleaseLabel->fNumeric = TRUE;
457 pReleaseLabel->dwValue = uLabel;
458 }
459
460 pReleaseLabel->cchLabelOffset = wzPartBegin - pVersion->sczVersion;
461 pReleaseLabel->cchLabel = cchLabel;
462
463 if (fTrailingDot)
464 {
465 ++wzPartEnd;
466 }
467 wzPartBegin = wzPartEnd;
468
469 if (fLastPart)
470 {
471 break;
472 }
473 }
474
475 fInvalid |= fExpectedReleaseLabels && (!pVersion->cReleaseLabels || fTrailingDot);
476
477 if (!fInvalid && wzPartBegin < wzEnd)
478 {
479 if (*wzPartBegin == L'+')
480 {
481 wzPartBegin = wzPartEnd = wzPartBegin + 1;
482 }
483 else
484 {
485 fInvalid = TRUE;
486 }
487 }
488
489 if (fInvalid && fStrict)
490 {
491 ExitFunction1(hr = E_INVALIDARG);
492 }
493
494 pVersion->cchMetadataOffset = min(wzPartBegin, wzEnd) - pVersion->sczVersion;
495 pVersion->fInvalid = fInvalid;
496
497 *ppVersion = pVersion;
498 pVersion = NULL;
499 hr = S_OK;
500
501LExit:
502 ReleaseVerutilVersion(pVersion);
503
504 return hr;
505}
506
507DAPI_(HRESULT) VerVersionFromQword(
508 __in DWORD64 qwVersion,
509 __out VERUTIL_VERSION** ppVersion
510 )
511{
512 HRESULT hr = S_OK;
513 VERUTIL_VERSION* pVersion = NULL;
514
515 pVersion = reinterpret_cast<VERUTIL_VERSION*>(MemAlloc(sizeof(VERUTIL_VERSION), TRUE));
516 VerExitOnNull(pVersion, hr, E_OUTOFMEMORY, "Failed to allocate memory for Verutil version from QWORD.");
517
518 pVersion->dwMajor = (WORD)(qwVersion >> 48 & 0xffff);
519 pVersion->dwMinor = (WORD)(qwVersion >> 32 & 0xffff);
520 pVersion->dwPatch = (WORD)(qwVersion >> 16 & 0xffff);
521 pVersion->dwRevision = (WORD)(qwVersion & 0xffff);
522
523 hr = StrAllocFormatted(&pVersion->sczVersion, L"%lu.%lu.%lu.%lu", pVersion->dwMajor, pVersion->dwMinor, pVersion->dwPatch, pVersion->dwRevision);
524 ExitOnFailure(hr, "Failed to allocate and format the version string.");
525
526 pVersion->cchMetadataOffset = lstrlenW(pVersion->sczVersion);
527
528 *ppVersion = pVersion;
529 pVersion = NULL;
530
531LExit:
532 ReleaseVerutilVersion(pVersion);
533
534 return hr;
535}
536
537
538static int CompareDword(
539 __in const DWORD& dw1,
540 __in const DWORD& dw2
541 )
542{
543 int nResult = 0;
544
545 if (dw1 > dw2)
546 {
547 nResult = 1;
548 }
549 else if (dw1 < dw2)
550 {
551 nResult = -1;
552 }
553
554 return nResult;
555}
556
557static HRESULT CompareReleaseLabel(
558 __in const VERUTIL_VERSION_RELEASE_LABEL* p1,
559 __in LPCWSTR wzVersion1,
560 __in const VERUTIL_VERSION_RELEASE_LABEL* p2,
561 __in LPCWSTR wzVersion2,
562 __out int* pnResult
563 )
564{
565 HRESULT hr = S_OK;
566 int nResult = 0;
567
568 if (p1 == p2)
569 {
570 ExitFunction();
571 }
572 else if (p1 && !p2)
573 {
574 ExitFunction1(nResult = 1);
575 }
576 else if (!p1 && p2)
577 {
578 ExitFunction1(nResult = -1);
579 }
580
581 if (p1->fNumeric)
582 {
583 if (p2->fNumeric)
584 {
585 nResult = CompareDword(p1->dwValue, p2->dwValue);
586 }
587 else
588 {
589 nResult = -1;
590 }
591 }
592 else
593 {
594 if (p2->fNumeric)
595 {
596 nResult = 1;
597 }
598 else
599 {
600 hr = CompareVersionSubstring(wzVersion1 + p1->cchLabelOffset, p1->cchLabel, wzVersion2 + p2->cchLabelOffset, p2->cchLabel, &nResult);
601 }
602 }
603
604LExit:
605 *pnResult = nResult;
606
607 return hr;
608}
609
610static HRESULT CompareVersionSubstring(
611 __in LPCWSTR wzString1,
612 __in int cchCount1,
613 __in LPCWSTR wzString2,
614 __in int cchCount2,
615 __out int* pnResult
616 )
617{
618 HRESULT hr = S_OK;
619 int nResult = 0;
620
621 nResult = ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzString1, cchCount1, wzString2, cchCount2);
622 if (!nResult)
623 {
624 VerExitOnLastError(hr, "Failed to compare version substrings");
625 }
626
627LExit:
628 *pnResult = nResult - 2;
629
630 return hr;
631}
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
index 4c660aa9..31b5a5c0 100644
--- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
@@ -49,6 +49,7 @@
49 <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " /> 49 <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
50 <ClCompile Include="StrUtilTest.cpp" /> 50 <ClCompile Include="StrUtilTest.cpp" />
51 <ClCompile Include="UriUtilTest.cpp" /> 51 <ClCompile Include="UriUtilTest.cpp" />
52 <ClCompile Include="VerUtilTests.cpp" />
52 </ItemGroup> 53 </ItemGroup>
53 <ItemGroup> 54 <ItemGroup>
54 <ClInclude Include="precomp.h" /> 55 <ClInclude Include="precomp.h" />
diff --git a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
index 0c83e3fa..fdc6d278 100644
--- a/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+++ b/src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
@@ -57,6 +57,9 @@
57 <ClCompile Include="UriUtilTest.cpp"> 57 <ClCompile Include="UriUtilTest.cpp">
58 <Filter>Source Files</Filter> 58 <Filter>Source Files</Filter>
59 </ClCompile> 59 </ClCompile>
60 <ClCompile Include="VerUtilTests.cpp">
61 <Filter>Source Files</Filter>
62 </ClCompile>
60 </ItemGroup> 63 </ItemGroup>
61 <ItemGroup> 64 <ItemGroup>
62 <ResourceCompile Include="UnitTest.rc"> 65 <ResourceCompile Include="UnitTest.rc">
diff --git a/src/test/DUtilUnitTest/VerUtilTests.cpp b/src/test/DUtilUnitTest/VerUtilTests.cpp
new file mode 100644
index 00000000..58b561e9
--- /dev/null
+++ b/src/test/DUtilUnitTest/VerUtilTests.cpp
@@ -0,0 +1,891 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5using namespace System;
6using namespace Xunit;
7using namespace WixBuildTools::TestSupport;
8
9namespace DutilTests
10{
11 public ref class VerUtil
12 {
13 public:
14 [Fact]
15 void VerCompareVersionsTreatsMissingRevisionAsZero()
16 {
17 HRESULT hr = S_OK;
18 VERUTIL_VERSION* pVersion1 = NULL;
19 VERUTIL_VERSION* pVersion2 = NULL;
20 VERUTIL_VERSION* pVersion3 = NULL;
21 LPCWSTR wzVersion1 = L"1.2.3.4";
22 LPCWSTR wzVersion2 = L"1.2.3";
23 LPCWSTR wzVersion3 = L"1.2.3.0";
24
25 try
26 {
27 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
28 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
29
30 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
31 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
32
33 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
34 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
35
36 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
37 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
38 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
39 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
40 Assert::Equal<DWORD>(4, pVersion1->dwRevision);
41 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
42 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
43 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
44
45 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
46 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
47 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
48 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
49 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
50 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
51 Assert::Equal<DWORD>(5, pVersion2->cchMetadataOffset);
52 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
53
54 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
55 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
56 Assert::Equal<DWORD>(2, pVersion3->dwMinor);
57 Assert::Equal<DWORD>(3, pVersion3->dwPatch);
58 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
59 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
60 Assert::Equal<DWORD>(7, pVersion3->cchMetadataOffset);
61 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
62
63 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
64 TestVerutilCompareParsedVersions(pVersion3, pVersion2, 0);
65 }
66 finally
67 {
68 ReleaseVerutilVersion(pVersion1);
69 ReleaseVerutilVersion(pVersion2);
70 ReleaseVerutilVersion(pVersion3);
71 }
72 }
73
74 [Fact]
75 void VerCompareVersionsTreatsNumericReleaseLabelsAsNumbers()
76 {
77 HRESULT hr = S_OK;
78 VERUTIL_VERSION* pVersion1 = NULL;
79 VERUTIL_VERSION* pVersion2 = NULL;
80 LPCWSTR wzVersion1 = L"1.0-2.0";
81 LPCWSTR wzVersion2 = L"1.0-19";
82
83 try
84 {
85 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
86 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
87
88 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
89 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
90
91 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
92 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
93 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
94 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
95 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
96 Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
97
98 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[0].fNumeric);
99 Assert::Equal<DWORD>(2, pVersion1->rgReleaseLabels[0].dwValue);
100 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
101 Assert::Equal<DWORD>(4, pVersion1->rgReleaseLabels[0].cchLabelOffset);
102
103 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
104 Assert::Equal<DWORD>(0, pVersion1->rgReleaseLabels[1].dwValue);
105 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
106 Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[1].cchLabelOffset);
107
108 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
109 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
110
111 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
112 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
113 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
114 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
115 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
116 Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
117
118 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
119 Assert::Equal<DWORD>(19, pVersion2->rgReleaseLabels[0].dwValue);
120 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabel);
121 Assert::Equal<DWORD>(4, pVersion2->rgReleaseLabels[0].cchLabelOffset);
122
123 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
124 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
125
126 TestVerutilCompareParsedVersions(pVersion1, pVersion2, -1);
127 }
128 finally
129 {
130 ReleaseVerutilVersion(pVersion1);
131 ReleaseVerutilVersion(pVersion2);
132 }
133 }
134
135 [Fact]
136 void VerCompareVersionsHandlesNormallyInvalidVersions()
137 {
138 HRESULT hr = S_OK;
139 VERUTIL_VERSION* pVersion1 = NULL;
140 VERUTIL_VERSION* pVersion2 = NULL;
141 VERUTIL_VERSION* pVersion3 = NULL;
142 VERUTIL_VERSION* pVersion4 = NULL;
143 VERUTIL_VERSION* pVersion5 = NULL;
144 VERUTIL_VERSION* pVersion6 = NULL;
145 LPCWSTR wzVersion1 = L"10.-4.0";
146 LPCWSTR wzVersion2 = L"10.-2.0";
147 LPCWSTR wzVersion3 = L"0";
148 LPCWSTR wzVersion4 = L"";
149 LPCWSTR wzVersion5 = L"10-2";
150 LPCWSTR wzVersion6 = L"10-4.@";
151
152 try
153 {
154 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
155 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
156
157 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
158 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
159
160 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
161 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
162
163 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
164 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
165
166 hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
167 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
168
169 hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
170 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
171
172 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
173 Assert::Equal<DWORD>(10, pVersion1->dwMajor);
174 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
175 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
176 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
177 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
178 Assert::Equal<DWORD>(3, pVersion1->cchMetadataOffset);
179 Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
180
181 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
182 Assert::Equal<DWORD>(10, pVersion2->dwMajor);
183 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
184 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
185 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
186 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
187 Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
188 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
189
190 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
191 Assert::Equal<DWORD>(0, pVersion3->dwMajor);
192 Assert::Equal<DWORD>(0, pVersion3->dwMinor);
193 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
194 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
195 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
196 Assert::Equal<DWORD>(1, pVersion3->cchMetadataOffset);
197 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
198
199 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
200 Assert::Equal<DWORD>(0, pVersion4->dwMajor);
201 Assert::Equal<DWORD>(0, pVersion4->dwMinor);
202 Assert::Equal<DWORD>(0, pVersion4->dwPatch);
203 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
204 Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
205 Assert::Equal<DWORD>(0, pVersion4->cchMetadataOffset);
206 Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
207
208 NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
209 Assert::Equal<DWORD>(10, pVersion5->dwMajor);
210 Assert::Equal<DWORD>(0, pVersion5->dwMinor);
211 Assert::Equal<DWORD>(0, pVersion5->dwPatch);
212 Assert::Equal<DWORD>(0, pVersion5->dwRevision);
213 Assert::Equal<DWORD>(1, pVersion5->cReleaseLabels);
214
215 Assert::Equal<BOOL>(TRUE, pVersion5->rgReleaseLabels[0].fNumeric);
216 Assert::Equal<DWORD>(2, pVersion5->rgReleaseLabels[0].dwValue);
217 Assert::Equal<DWORD>(1, pVersion5->rgReleaseLabels[0].cchLabel);
218 Assert::Equal<DWORD>(3, pVersion5->rgReleaseLabels[0].cchLabelOffset);
219
220 Assert::Equal<DWORD>(4, pVersion5->cchMetadataOffset);
221 Assert::Equal<BOOL>(FALSE, pVersion5->fInvalid);
222
223 NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
224 Assert::Equal<DWORD>(10, pVersion6->dwMajor);
225 Assert::Equal<DWORD>(0, pVersion6->dwMinor);
226 Assert::Equal<DWORD>(0, pVersion6->dwPatch);
227 Assert::Equal<DWORD>(0, pVersion6->dwRevision);
228 Assert::Equal<DWORD>(1, pVersion6->cReleaseLabels);
229
230 Assert::Equal<BOOL>(TRUE, pVersion6->rgReleaseLabels[0].fNumeric);
231 Assert::Equal<DWORD>(4, pVersion6->rgReleaseLabels[0].dwValue);
232 Assert::Equal<DWORD>(1, pVersion6->rgReleaseLabels[0].cchLabel);
233 Assert::Equal<DWORD>(3, pVersion6->rgReleaseLabels[0].cchLabelOffset);
234
235 Assert::Equal<DWORD>(5, pVersion6->cchMetadataOffset);
236 Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
237
238 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
239 TestVerutilCompareParsedVersions(pVersion3, pVersion4, 1);
240 TestVerutilCompareParsedVersions(pVersion5, pVersion6, 1);
241 }
242 finally
243 {
244 ReleaseVerutilVersion(pVersion1);
245 ReleaseVerutilVersion(pVersion2);
246 ReleaseVerutilVersion(pVersion3);
247 ReleaseVerutilVersion(pVersion4);
248 ReleaseVerutilVersion(pVersion5);
249 ReleaseVerutilVersion(pVersion6);
250 }
251 }
252
253 [Fact]
254 void VerCompareVersionsTreatsHyphenAsVersionSeparator()
255 {
256 HRESULT hr = S_OK;
257 VERUTIL_VERSION* pVersion1 = NULL;
258 VERUTIL_VERSION* pVersion2 = NULL;
259 VERUTIL_VERSION* pVersion3 = NULL;
260 LPCWSTR wzVersion1 = L"0.0.1-a";
261 LPCWSTR wzVersion2 = L"0-2";
262 LPCWSTR wzVersion3 = L"1-2";
263
264 try
265 {
266 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
267 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
268
269 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
270 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
271
272 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
273 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
274
275 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
276 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
277 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
278 Assert::Equal<DWORD>(1, pVersion1->dwPatch);
279 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
280 Assert::Equal<DWORD>(1, pVersion1->cReleaseLabels);
281
282 Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
283 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
284 Assert::Equal<DWORD>(6, pVersion1->rgReleaseLabels[0].cchLabelOffset);
285
286 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
287 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
288
289 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
290 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
291 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
292 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
293 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
294 Assert::Equal<DWORD>(1, pVersion2->cReleaseLabels);
295
296 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[0].fNumeric);
297 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].dwValue);
298 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
299 Assert::Equal<DWORD>(2, pVersion2->rgReleaseLabels[0].cchLabelOffset);
300
301 Assert::Equal<DWORD>(3, pVersion2->cchMetadataOffset);
302 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
303
304 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
305 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
306 Assert::Equal<DWORD>(0, pVersion3->dwMinor);
307 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
308 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
309 Assert::Equal<DWORD>(1, pVersion3->cReleaseLabels);
310
311 Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[0].fNumeric);
312 Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].dwValue);
313 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
314 Assert::Equal<DWORD>(2, pVersion3->rgReleaseLabels[0].cchLabelOffset);
315
316 Assert::Equal<DWORD>(3, pVersion3->cchMetadataOffset);
317 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
318
319 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
320 TestVerutilCompareParsedVersions(pVersion1, pVersion3, -1);
321 }
322 finally
323 {
324 ReleaseVerutilVersion(pVersion1);
325 ReleaseVerutilVersion(pVersion2);
326 ReleaseVerutilVersion(pVersion3);
327 }
328 }
329
330 [Fact]
331 void VerCompareVersionsIgnoresLeadingZeroes()
332 {
333 HRESULT hr = S_OK;
334 VERUTIL_VERSION* pVersion1 = NULL;
335 VERUTIL_VERSION* pVersion2 = NULL;
336 VERUTIL_VERSION* pVersion3 = NULL;
337 VERUTIL_VERSION* pVersion4 = NULL;
338 LPCWSTR wzVersion1 = L"0.01-a.1";
339 LPCWSTR wzVersion2 = L"0.1.0-a.1";
340 LPCWSTR wzVersion3 = L"0.1-a.b.0";
341 LPCWSTR wzVersion4 = L"0.1.0-a.b.000";
342
343 try
344 {
345 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
346 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
347
348 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
349 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
350
351 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
352 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
353
354 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
355 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
356
357 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
358 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
359 Assert::Equal<DWORD>(1, pVersion1->dwMinor);
360 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
361 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
362 Assert::Equal<DWORD>(2, pVersion1->cReleaseLabels);
363
364 Assert::Equal<BOOL>(FALSE, pVersion1->rgReleaseLabels[0].fNumeric);
365 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[0].cchLabel);
366 Assert::Equal<DWORD>(5, pVersion1->rgReleaseLabels[0].cchLabelOffset);
367
368 Assert::Equal<BOOL>(TRUE, pVersion1->rgReleaseLabels[1].fNumeric);
369 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].dwValue);
370 Assert::Equal<DWORD>(1, pVersion1->rgReleaseLabels[1].cchLabel);
371 Assert::Equal<DWORD>(7, pVersion1->rgReleaseLabels[1].cchLabelOffset);
372
373 Assert::Equal<DWORD>(8, pVersion1->cchMetadataOffset);
374 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
375
376 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
377 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
378 Assert::Equal<DWORD>(1, pVersion2->dwMinor);
379 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
380 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
381 Assert::Equal<DWORD>(2, pVersion2->cReleaseLabels);
382
383 Assert::Equal<BOOL>(FALSE, pVersion2->rgReleaseLabels[0].fNumeric);
384 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[0].cchLabel);
385 Assert::Equal<DWORD>(6, pVersion2->rgReleaseLabels[0].cchLabelOffset);
386
387 Assert::Equal<BOOL>(TRUE, pVersion2->rgReleaseLabels[1].fNumeric);
388 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].dwValue);
389 Assert::Equal<DWORD>(1, pVersion2->rgReleaseLabels[1].cchLabel);
390 Assert::Equal<DWORD>(8, pVersion2->rgReleaseLabels[1].cchLabelOffset);
391
392 Assert::Equal<DWORD>(9, pVersion2->cchMetadataOffset);
393 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
394
395 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
396 Assert::Equal<DWORD>(0, pVersion3->dwMajor);
397 Assert::Equal<DWORD>(1, pVersion3->dwMinor);
398 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
399 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
400 Assert::Equal<DWORD>(3, pVersion3->cReleaseLabels);
401
402 Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[0].fNumeric);
403 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[0].cchLabel);
404 Assert::Equal<DWORD>(4, pVersion3->rgReleaseLabels[0].cchLabelOffset);
405
406 Assert::Equal<BOOL>(FALSE, pVersion3->rgReleaseLabels[1].fNumeric);
407 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[1].cchLabel);
408 Assert::Equal<DWORD>(6, pVersion3->rgReleaseLabels[1].cchLabelOffset);
409
410 Assert::Equal<BOOL>(TRUE, pVersion3->rgReleaseLabels[2].fNumeric);
411 Assert::Equal<DWORD>(0, pVersion3->rgReleaseLabels[2].dwValue);
412 Assert::Equal<DWORD>(1, pVersion3->rgReleaseLabels[2].cchLabel);
413 Assert::Equal<DWORD>(8, pVersion3->rgReleaseLabels[2].cchLabelOffset);
414
415 Assert::Equal<DWORD>(9, pVersion3->cchMetadataOffset);
416 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
417
418 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
419 Assert::Equal<DWORD>(0, pVersion4->dwMajor);
420 Assert::Equal<DWORD>(1, pVersion4->dwMinor);
421 Assert::Equal<DWORD>(0, pVersion4->dwPatch);
422 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
423 Assert::Equal<DWORD>(3, pVersion4->cReleaseLabels);
424
425 Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[0].fNumeric);
426 Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[0].cchLabel);
427 Assert::Equal<DWORD>(6, pVersion4->rgReleaseLabels[0].cchLabelOffset);
428
429 Assert::Equal<BOOL>(FALSE, pVersion4->rgReleaseLabels[1].fNumeric);
430 Assert::Equal<DWORD>(1, pVersion4->rgReleaseLabels[1].cchLabel);
431 Assert::Equal<DWORD>(8, pVersion4->rgReleaseLabels[1].cchLabelOffset);
432
433 Assert::Equal<BOOL>(TRUE, pVersion4->rgReleaseLabels[2].fNumeric);
434 Assert::Equal<DWORD>(0, pVersion4->rgReleaseLabels[2].dwValue);
435 Assert::Equal<DWORD>(3, pVersion4->rgReleaseLabels[2].cchLabel);
436 Assert::Equal<DWORD>(10, pVersion4->rgReleaseLabels[2].cchLabelOffset);
437
438 Assert::Equal<DWORD>(13, pVersion4->cchMetadataOffset);
439 Assert::Equal<BOOL>(FALSE, pVersion4->fInvalid);
440
441 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
442 TestVerutilCompareParsedVersions(pVersion3, pVersion4, 0);
443 }
444 finally
445 {
446 ReleaseVerutilVersion(pVersion1);
447 ReleaseVerutilVersion(pVersion2);
448 ReleaseVerutilVersion(pVersion3);
449 ReleaseVerutilVersion(pVersion4);
450 }
451 }
452
453 [Fact]
454 void VerCompareVersionsTreatsUnexpectedContentAsMetadata()
455 {
456 HRESULT hr = S_OK;
457 VERUTIL_VERSION* pVersion1 = NULL;
458 VERUTIL_VERSION* pVersion2 = NULL;
459 VERUTIL_VERSION* pVersion3 = NULL;
460 LPCWSTR wzVersion1 = L"1.2.3+abcd";
461 LPCWSTR wzVersion2 = L"1.2.3.abcd";
462 LPCWSTR wzVersion3 = L"1.2.3.-abcd";
463
464 try
465 {
466 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
467 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
468
469 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
470 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
471
472 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
473 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
474
475 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
476 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
477 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
478 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
479 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
480 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
481 Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
482 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
483
484 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
485 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
486 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
487 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
488 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
489 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
490 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
491 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
492
493 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
494 Assert::Equal<DWORD>(1, pVersion3->dwMajor);
495 Assert::Equal<DWORD>(2, pVersion3->dwMinor);
496 Assert::Equal<DWORD>(3, pVersion3->dwPatch);
497 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
498 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
499 Assert::Equal<DWORD>(6, pVersion3->cchMetadataOffset);
500 Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
501
502 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
503 TestVerutilCompareParsedVersions(pVersion1, pVersion3, 1);
504 TestVerutilCompareParsedVersions(pVersion2, pVersion3, -1);
505 }
506 finally
507 {
508 ReleaseVerutilVersion(pVersion1);
509 ReleaseVerutilVersion(pVersion2);
510 ReleaseVerutilVersion(pVersion3);
511 }
512 }
513
514 [Fact]
515 void VerCompareVersionsIgnoresLeadingV()
516 {
517 HRESULT hr = S_OK;
518 VERUTIL_VERSION* pVersion1 = NULL;
519 VERUTIL_VERSION* pVersion2 = NULL;
520 VERUTIL_VERSION* pVersion3 = NULL;
521 LPCWSTR wzVersion1 = L"10.20.30.40";
522 LPCWSTR wzVersion2 = L"v10.20.30.40";
523 LPCWSTR wzVersion3 = L"V10.20.30.40";
524
525 try
526 {
527 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
528 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
529
530 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
531 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
532
533 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
534 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
535
536 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
537 Assert::Equal<DWORD>(10, pVersion1->dwMajor);
538 Assert::Equal<DWORD>(20, pVersion1->dwMinor);
539 Assert::Equal<DWORD>(30, pVersion1->dwPatch);
540 Assert::Equal<DWORD>(40, pVersion1->dwRevision);
541 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
542 Assert::Equal<DWORD>(11, pVersion1->cchMetadataOffset);
543 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
544
545 NativeAssert::StringEqual(wzVersion1, pVersion2->sczVersion);
546 Assert::Equal<DWORD>(10, pVersion2->dwMajor);
547 Assert::Equal<DWORD>(20, pVersion2->dwMinor);
548 Assert::Equal<DWORD>(30, pVersion2->dwPatch);
549 Assert::Equal<DWORD>(40, pVersion2->dwRevision);
550 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
551 Assert::Equal<DWORD>(11, pVersion2->cchMetadataOffset);
552 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
553
554 NativeAssert::StringEqual(wzVersion1, pVersion3->sczVersion);
555 Assert::Equal<DWORD>(10, pVersion3->dwMajor);
556 Assert::Equal<DWORD>(20, pVersion3->dwMinor);
557 Assert::Equal<DWORD>(30, pVersion3->dwPatch);
558 Assert::Equal<DWORD>(40, pVersion3->dwRevision);
559 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
560 Assert::Equal<DWORD>(11, pVersion3->cchMetadataOffset);
561 Assert::Equal<BOOL>(FALSE, pVersion3->fInvalid);
562
563 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
564 TestVerutilCompareParsedVersions(pVersion1, pVersion3, 0);
565 }
566 finally
567 {
568 ReleaseVerutilVersion(pVersion1);
569 ReleaseVerutilVersion(pVersion2);
570 ReleaseVerutilVersion(pVersion3);
571 }
572 }
573
574 [Fact]
575 void VerCompareVersionsHandlesTooLargeNumbers()
576 {
577 HRESULT hr = S_OK;
578 VERUTIL_VERSION* pVersion1 = NULL;
579 VERUTIL_VERSION* pVersion2 = NULL;
580 LPCWSTR wzVersion1 = L"4294967295.4294967295.4294967295.4294967295";
581 LPCWSTR wzVersion2 = L"4294967296.4294967296.4294967296.4294967296";
582
583 try
584 {
585 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
586 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
587
588 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
589 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
590
591 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
592 Assert::Equal<DWORD>(4294967295, pVersion1->dwMajor);
593 Assert::Equal<DWORD>(4294967295, pVersion1->dwMinor);
594 Assert::Equal<DWORD>(4294967295, pVersion1->dwPatch);
595 Assert::Equal<DWORD>(4294967295, pVersion1->dwRevision);
596 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
597 Assert::Equal<DWORD>(43, pVersion1->cchMetadataOffset);
598 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
599
600 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
601 Assert::Equal<DWORD>(0, pVersion2->dwMajor);
602 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
603 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
604 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
605 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
606 Assert::Equal<DWORD>(0, pVersion2->cchMetadataOffset);
607 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
608
609 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 1);
610 }
611 finally
612 {
613 ReleaseVerutilVersion(pVersion1);
614 ReleaseVerutilVersion(pVersion2);
615 }
616 }
617
618 [Fact]
619 void VerCompareVersionsIgnoresMetadataForValidVersions()
620 {
621 HRESULT hr = S_OK;
622 VERUTIL_VERSION* pVersion1 = NULL;
623 VERUTIL_VERSION* pVersion2 = NULL;
624 LPCWSTR wzVersion1 = L"1.2.3+abc";
625 LPCWSTR wzVersion2 = L"1.2.3+xyz";
626
627 try
628 {
629 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
630 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
631
632 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
633 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
634
635 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
636 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
637 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
638 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
639 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
640 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
641 Assert::Equal<DWORD>(6, pVersion1->cchMetadataOffset);
642 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
643
644 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
645 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
646 Assert::Equal<DWORD>(2, pVersion2->dwMinor);
647 Assert::Equal<DWORD>(3, pVersion2->dwPatch);
648 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
649 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
650 Assert::Equal<DWORD>(6, pVersion2->cchMetadataOffset);
651 Assert::Equal<BOOL>(FALSE, pVersion2->fInvalid);
652
653 TestVerutilCompareParsedVersions(pVersion1, pVersion2, 0);
654 }
655 finally
656 {
657 ReleaseVerutilVersion(pVersion1);
658 ReleaseVerutilVersion(pVersion2);
659 }
660 }
661
662 [Fact]
663 void VerCopyVersionCopiesVersion()
664 {
665 HRESULT hr = S_OK;
666 LPCWSTR wzVersion = L"1.2.3.4-a.b.c.d.5.+abc123";
667 VERUTIL_VERSION* pSource = NULL;
668 VERUTIL_VERSION* pCopy = NULL;
669 int nResult = 0;
670
671 try
672 {
673 hr = VerParseVersion(wzVersion, 0, FALSE, &pSource);
674 NativeAssert::Succeeded(hr, "VerParseVersion failed");
675
676 NativeAssert::StringEqual(wzVersion, pSource->sczVersion);
677 Assert::Equal<DWORD>(1, pSource->dwMajor);
678 Assert::Equal<DWORD>(2, pSource->dwMinor);
679 Assert::Equal<DWORD>(3, pSource->dwPatch);
680 Assert::Equal<DWORD>(4, pSource->dwRevision);
681 Assert::Equal<DWORD>(5, pSource->cReleaseLabels);
682
683 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[0].fNumeric);
684 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[0].cchLabel);
685 Assert::Equal<DWORD>(8, pSource->rgReleaseLabels[0].cchLabelOffset);
686
687 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[1].fNumeric);
688 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[1].cchLabel);
689 Assert::Equal<DWORD>(10, pSource->rgReleaseLabels[1].cchLabelOffset);
690
691 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[2].fNumeric);
692 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[2].cchLabel);
693 Assert::Equal<DWORD>(12, pSource->rgReleaseLabels[2].cchLabelOffset);
694
695 Assert::Equal<BOOL>(FALSE, pSource->rgReleaseLabels[3].fNumeric);
696 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[3].cchLabel);
697 Assert::Equal<DWORD>(14, pSource->rgReleaseLabels[3].cchLabelOffset);
698
699 Assert::Equal<BOOL>(TRUE, pSource->rgReleaseLabels[4].fNumeric);
700 Assert::Equal<DWORD>(5, pSource->rgReleaseLabels[4].dwValue);
701 Assert::Equal<DWORD>(1, pSource->rgReleaseLabels[4].cchLabel);
702 Assert::Equal<DWORD>(16, pSource->rgReleaseLabels[4].cchLabelOffset);
703
704 Assert::Equal<DWORD>(18, pSource->cchMetadataOffset);
705 Assert::Equal<BOOL>(TRUE, pSource->fInvalid);
706
707 hr = VerCopyVersion(pSource, &pCopy);
708 NativeAssert::Succeeded(hr, "VerCopyVersion failed");
709
710 Assert::False(pSource == pCopy);
711 Assert::False(pSource->sczVersion == pCopy->sczVersion);
712 Assert::False(pSource->rgReleaseLabels == pCopy->rgReleaseLabels);
713
714 hr = VerCompareParsedVersions(pSource, pCopy, &nResult);
715 NativeAssert::Succeeded(hr, "VerCompareParsedVersions failed");
716
717 Assert::Equal<int>(nResult, 0);
718 }
719 finally
720 {
721 ReleaseVerutilVersion(pCopy);
722 ReleaseVerutilVersion(pSource);
723 }
724 }
725
726 [Fact]
727 void VerParseVersionTreatsTrailingDotsAsInvalid()
728 {
729 HRESULT hr = S_OK;
730 VERUTIL_VERSION* pVersion1 = NULL;
731 VERUTIL_VERSION* pVersion2 = NULL;
732 VERUTIL_VERSION* pVersion3 = NULL;
733 VERUTIL_VERSION* pVersion4 = NULL;
734 VERUTIL_VERSION* pVersion5 = NULL;
735 VERUTIL_VERSION* pVersion6 = NULL;
736 VERUTIL_VERSION* pVersion7 = NULL;
737 LPCWSTR wzVersion1 = L".";
738 LPCWSTR wzVersion2 = L"1.";
739 LPCWSTR wzVersion3 = L"2.1.";
740 LPCWSTR wzVersion4 = L"3.2.1.";
741 LPCWSTR wzVersion5 = L"4.3.2.1.";
742 LPCWSTR wzVersion6 = L"5-.";
743 LPCWSTR wzVersion7 = L"6-a.";
744
745 try
746 {
747 hr = VerParseVersion(wzVersion1, 0, FALSE, &pVersion1);
748 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion1);
749
750 hr = VerParseVersion(wzVersion2, 0, FALSE, &pVersion2);
751 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion2);
752
753 hr = VerParseVersion(wzVersion3, 0, FALSE, &pVersion3);
754 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion3);
755
756 hr = VerParseVersion(wzVersion4, 0, FALSE, &pVersion4);
757 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion4);
758
759 hr = VerParseVersion(wzVersion5, 0, FALSE, &pVersion5);
760 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion5);
761
762 hr = VerParseVersion(wzVersion6, 0, FALSE, &pVersion6);
763 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion6);
764
765 hr = VerParseVersion(wzVersion7, 0, FALSE, &pVersion7);
766 NativeAssert::Succeeded(hr, "Failed to parse version '{0}'", wzVersion7);
767
768 NativeAssert::StringEqual(wzVersion1, pVersion1->sczVersion);
769 Assert::Equal<DWORD>(0, pVersion1->dwMajor);
770 Assert::Equal<DWORD>(0, pVersion1->dwMinor);
771 Assert::Equal<DWORD>(0, pVersion1->dwPatch);
772 Assert::Equal<DWORD>(0, pVersion1->dwRevision);
773 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
774 Assert::Equal<DWORD>(0, pVersion1->cchMetadataOffset);
775 Assert::Equal<BOOL>(TRUE, pVersion1->fInvalid);
776
777 NativeAssert::StringEqual(wzVersion2, pVersion2->sczVersion);
778 Assert::Equal<DWORD>(1, pVersion2->dwMajor);
779 Assert::Equal<DWORD>(0, pVersion2->dwMinor);
780 Assert::Equal<DWORD>(0, pVersion2->dwPatch);
781 Assert::Equal<DWORD>(0, pVersion2->dwRevision);
782 Assert::Equal<DWORD>(0, pVersion2->cReleaseLabels);
783 Assert::Equal<DWORD>(2, pVersion2->cchMetadataOffset);
784 Assert::Equal<BOOL>(TRUE, pVersion2->fInvalid);
785
786 NativeAssert::StringEqual(wzVersion3, pVersion3->sczVersion);
787 Assert::Equal<DWORD>(2, pVersion3->dwMajor);
788 Assert::Equal<DWORD>(1, pVersion3->dwMinor);
789 Assert::Equal<DWORD>(0, pVersion3->dwPatch);
790 Assert::Equal<DWORD>(0, pVersion3->dwRevision);
791 Assert::Equal<DWORD>(0, pVersion3->cReleaseLabels);
792 Assert::Equal<DWORD>(4, pVersion3->cchMetadataOffset);
793 Assert::Equal<BOOL>(TRUE, pVersion3->fInvalid);
794
795 NativeAssert::StringEqual(wzVersion4, pVersion4->sczVersion);
796 Assert::Equal<DWORD>(3, pVersion4->dwMajor);
797 Assert::Equal<DWORD>(2, pVersion4->dwMinor);
798 Assert::Equal<DWORD>(1, pVersion4->dwPatch);
799 Assert::Equal<DWORD>(0, pVersion4->dwRevision);
800 Assert::Equal<DWORD>(0, pVersion4->cReleaseLabels);
801 Assert::Equal<DWORD>(6, pVersion4->cchMetadataOffset);
802 Assert::Equal<BOOL>(TRUE, pVersion4->fInvalid);
803
804 NativeAssert::StringEqual(wzVersion5, pVersion5->sczVersion);
805 Assert::Equal<DWORD>(4, pVersion5->dwMajor);
806 Assert::Equal<DWORD>(3, pVersion5->dwMinor);
807 Assert::Equal<DWORD>(2, pVersion5->dwPatch);
808 Assert::Equal<DWORD>(1, pVersion5->dwRevision);
809 Assert::Equal<DWORD>(0, pVersion5->cReleaseLabels);
810 Assert::Equal<DWORD>(8, pVersion5->cchMetadataOffset);
811 Assert::Equal<BOOL>(TRUE, pVersion5->fInvalid);
812
813 NativeAssert::StringEqual(wzVersion6, pVersion6->sczVersion);
814 Assert::Equal<DWORD>(5, pVersion6->dwMajor);
815 Assert::Equal<DWORD>(0, pVersion6->dwMinor);
816 Assert::Equal<DWORD>(0, pVersion6->dwPatch);
817 Assert::Equal<DWORD>(0, pVersion6->dwRevision);
818 Assert::Equal<DWORD>(0, pVersion6->cReleaseLabels);
819 Assert::Equal<DWORD>(2, pVersion6->cchMetadataOffset);
820 Assert::Equal<BOOL>(TRUE, pVersion6->fInvalid);
821
822 NativeAssert::StringEqual(wzVersion7, pVersion7->sczVersion);
823 Assert::Equal<DWORD>(6, pVersion7->dwMajor);
824 Assert::Equal<DWORD>(0, pVersion7->dwMinor);
825 Assert::Equal<DWORD>(0, pVersion7->dwPatch);
826 Assert::Equal<DWORD>(0, pVersion7->dwRevision);
827 Assert::Equal<DWORD>(1, pVersion7->cReleaseLabels);
828
829 Assert::Equal<BOOL>(FALSE, pVersion7->rgReleaseLabels[0].fNumeric);
830 Assert::Equal<DWORD>(1, pVersion7->rgReleaseLabels[0].cchLabel);
831 Assert::Equal<DWORD>(2, pVersion7->rgReleaseLabels[0].cchLabelOffset);
832
833 Assert::Equal<DWORD>(4, pVersion7->cchMetadataOffset);
834 Assert::Equal<BOOL>(TRUE, pVersion7->fInvalid);
835 }
836 finally
837 {
838 ReleaseVerutilVersion(pVersion1);
839 ReleaseVerutilVersion(pVersion2);
840 ReleaseVerutilVersion(pVersion3);
841 ReleaseVerutilVersion(pVersion4);
842 ReleaseVerutilVersion(pVersion5);
843 ReleaseVerutilVersion(pVersion6);
844 ReleaseVerutilVersion(pVersion7);
845 }
846 }
847
848 [Fact]
849 void VerVersionFromQwordCreatesVersion()
850 {
851 HRESULT hr = S_OK;
852 VERUTIL_VERSION* pVersion1 = NULL;
853
854 try
855 {
856 hr = VerVersionFromQword(MAKEQWORDVERSION(1, 2, 3, 4), &pVersion1);
857 NativeAssert::Succeeded(hr, "VerVersionFromQword failed");
858
859 NativeAssert::StringEqual(L"1.2.3.4", pVersion1->sczVersion);
860 Assert::Equal<DWORD>(1, pVersion1->dwMajor);
861 Assert::Equal<DWORD>(2, pVersion1->dwMinor);
862 Assert::Equal<DWORD>(3, pVersion1->dwPatch);
863 Assert::Equal<DWORD>(4, pVersion1->dwRevision);
864 Assert::Equal<DWORD>(0, pVersion1->cReleaseLabels);
865 Assert::Equal<DWORD>(7, pVersion1->cchMetadataOffset);
866 Assert::Equal<BOOL>(FALSE, pVersion1->fInvalid);
867 }
868 finally
869 {
870 ReleaseVerutilVersion(pVersion1);
871 }
872 }
873
874 private:
875 void TestVerutilCompareParsedVersions(VERUTIL_VERSION* pVersion1, VERUTIL_VERSION* pVersion2, int nExpectedResult)
876 {
877 HRESULT hr = S_OK;
878 int nResult = 0;
879
880 hr = VerCompareParsedVersions(pVersion1, pVersion2, &nResult);
881 NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
882
883 Assert::Equal(nExpectedResult, nResult);
884
885 hr = VerCompareParsedVersions(pVersion2, pVersion1, &nResult);
886 NativeAssert::Succeeded(hr, "Failed to compare versions '{0}' and '{1}'", pVersion1->sczVersion, pVersion2->sczVersion);
887
888 Assert::Equal(nExpectedResult, -nResult);
889 }
890 };
891}
diff --git a/src/test/DUtilUnitTest/precomp.h b/src/test/DUtilUnitTest/precomp.h
index b3a1a9cb..f665fed1 100644
--- a/src/test/DUtilUnitTest/precomp.h
+++ b/src/test/DUtilUnitTest/precomp.h
@@ -22,6 +22,7 @@
22#include <monutil.h> 22#include <monutil.h>
23#include <regutil.h> 23#include <regutil.h>
24#include <uriutil.h> 24#include <uriutil.h>
25#include <verutil.h>
25 26
26#pragma managed 27#pragma managed
27#include <vcclr.h> 28#include <vcclr.h>