@joebigelow / wix / commits / d1d31466

WIXFEAT:6210 Parse and compare bundle versions kind of like SemVer.

Sean Hall committed Aug 29, 2020 at 21:29 UTC d1d31466bb9f2e887a277807d60378afef9cc57d
10 files changed +1630
src/dutil/dutil.vcxproj
+2
@@ -111,6 +111,7 @@
111 <ClCompile Include="uncutil.cpp" />
112 <ClCompile Include="uriutil.cpp" />
113 <ClCompile Include="userutil.cpp" />
114 + <ClCompile Include="verutil.cpp" />
115 <ClCompile Include="wiutil.cpp" />
116 <ClCompile Include="wuautil.cpp" />
117 <ClCompile Include="xmlutil.cpp" />
@@ -167,6 +168,7 @@
168 <ClInclude Include="inc\timeutil.h" />
169 <ClInclude Include="inc\uriutil.h" />
170 <ClInclude Include="inc\userutil.h" />
171 + <ClInclude Include="inc\verutil.h" />
172 <ClInclude Include="inc\wiutil.h" />
173 <ClInclude Include="inc\wuautil.h" />
174 <ClInclude Include="inc\xmlutil.h" />
src/dutil/dutil.vcxproj.filters
+6
@@ -159,6 +159,9 @@
159 <ClCompile Include="userutil.cpp">
160 <Filter>Source Files</Filter>
161 </ClCompile>
162 + <ClCompile Include="verutil.cpp">
163 + <Filter>Source Files</Filter>
164 + </ClCompile>
165 <ClCompile Include="wiutil.cpp">
166 <Filter>Source Files</Filter>
167 </ClCompile>
@@ -329,6 +332,9 @@
332 <ClInclude Include="inc\userutil.h">
333 <Filter>Header Files</Filter>
334 </ClInclude>
335 + <ClInclude Include="inc\verutil.h">
336 + <Filter>Header Files</Filter>
337 + </ClInclude>
338 <ClInclude Include="inc\wiutil.h">
339 <Filter>Header Files</Filter>
340 </ClInclude>
src/dutil/inc/dutilsources.h
+1
@@ -60,6 +60,7 @@ typedef enum DUTIL_SOURCE
60 DUTIL_SOURCE_WIUTIL,
61 DUTIL_SOURCE_WUAUTIL,
62 DUTIL_SOURCE_XMLUTIL,
63 + DUTIL_SOURCE_VERUTIL,
64
65 DUTIL_SOURCE_EXTERNAL = 256,
66 } DUTIL_SOURCE;
src/dutil/inc/verutil.h new
+93
@@ -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
6 +extern "C" {
7 +#endif
8 +
9 +#define ReleaseVerutilVersion(p) if (p) { VerFreeVersion(p); p = NULL; }
10 +
11 +typedef 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 +
19 +typedef 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 +*******************************************************************/
36 +HRESULT 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 +*******************************************************************/
47 +HRESULT 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 +*******************************************************************/
58 +HRESULT 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 +*******************************************************************/
67 +void DAPI VerFreeVersion(
68 + __in VERUTIL_VERSION* pVersion
69 + );
70 +
71 +/*******************************************************************
72 + VerParseVersion - parses the string into a Verutil version.
73 +
74 +*******************************************************************/
75 +HRESULT 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 +*******************************************************************/
86 +HRESULT DAPI VerVersionFromQword(
87 + __in DWORD64 qwVersion,
88 + __out VERUTIL_VERSION** ppVersion
89 + );
90 +
91 +#ifdef __cplusplus
92 +}
93 +#endif
src/dutil/precomp.h
+1
@@ -89,6 +89,7 @@
89 #include "uncutil.h"
90 #include "uriutil.h"
91 #include "userutil.h"
92 +#include "verutil.h"
93 #include "wiutil.h"
94 #include "wuautil.h"
95 #include <comutil.h> // This header is needed for msxml2.h to compile correctly
src/dutil/verutil.cpp new
+631
@@ -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
19 +const DWORD GROW_RELEASE_LABELS = 3;
20 +
21 +// Forward declarations.
22 +static int CompareDword(
23 + __in const DWORD& dw1,
24 + __in const DWORD& dw2
25 + );
26 +static 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 + );
33 +static 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 +
42 +DAPI_(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 +
140 +LExit:
141 + *pnResult = nResult;
142 + return hr;
143 +}
144 +
145 +DAPI_(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 +
166 +LExit:
167 + *pnResult = nResult;
168 +
169 + ReleaseVerutilVersion(pVersion1);
170 + ReleaseVerutilVersion(pVersion2);
171 +
172 + return hr;
173 +}
174 +
175 +DAPI_(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 +
216 +LExit:
217 + ReleaseVerutilVersion(pCopy);
218 +
219 + return hr;
220 +}
221 +
222 +DAPI_(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 +
234 +DAPI_(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 +
501 +LExit:
502 + ReleaseVerutilVersion(pVersion);
503 +
504 + return hr;
505 +}
506 +
507 +DAPI_(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 +
531 +LExit:
532 + ReleaseVerutilVersion(pVersion);
533 +
534 + return hr;
535 +}
536 +
537 +
538 +static 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 +
557 +static 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 +
604 +LExit:
605 + *pnResult = nResult;
606 +
607 + return hr;
608 +}
609 +
610 +static 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 +
627 +LExit:
628 + *pnResult = nResult - 2;
629 +
630 + return hr;
631 +}
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+1
@@ -49,6 +49,7 @@
49 <ClCompile Include="SceUtilTest.cpp" Condition=" Exists('$(SqlCESdkIncludePath)') " />
50 <ClCompile Include="StrUtilTest.cpp" />
51 <ClCompile Include="UriUtilTest.cpp" />
52 + <ClCompile Include="VerUtilTests.cpp" />
53 </ItemGroup>
54 <ItemGroup>
55 <ClInclude Include="precomp.h" />
src/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters
+3
@@ -57,6 +57,9 @@
57 <ClCompile Include="UriUtilTest.cpp">
58 <Filter>Source Files</Filter>
59 </ClCompile>
60 + <ClCompile Include="VerUtilTests.cpp">
61 + <Filter>Source Files</Filter>
62 + </ClCompile>
63 </ItemGroup>
64 <ItemGroup>
65 <ResourceCompile Include="UnitTest.rc">
src/test/DUtilUnitTest/VerUtilTests.cpp new
+891
@@ -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 +
5 +using namespace System;
6 +using namespace Xunit;
7 +using namespace WixBuildTools::TestSupport;
8 +
9 +namespace 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 +}
src/test/DUtilUnitTest/precomp.h
+1
@@ -22,6 +22,7 @@
22 #include <monutil.h>
23 #include <regutil.h>
24 #include <uriutil.h>
25 +#include <verutil.h>
26
27 #pragma managed
28 #include <vcclr.h>