2006-06-14 14:19:50 +02:00
|
|
|
/*
|
|
|
|
* Unit test suite for version functions
|
|
|
|
*
|
|
|
|
* Copyright 2006 Robert Shearman
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2012-05-30 18:40:34 +02:00
|
|
|
/* Needed for PRODUCT_* defines and GetProductInfo() */
|
|
|
|
#define _WIN32_WINNT 0x0600
|
|
|
|
|
2006-06-14 14:19:50 +02:00
|
|
|
#include "wine/test.h"
|
|
|
|
#include "winbase.h"
|
2014-10-06 22:31:43 +02:00
|
|
|
#include "winternl.h"
|
2006-06-14 14:19:50 +02:00
|
|
|
|
2012-05-30 18:40:34 +02:00
|
|
|
static BOOL (WINAPI * pGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD *);
|
2018-07-13 10:48:03 +02:00
|
|
|
static UINT (WINAPI * pGetSystemFirmwareTable)(DWORD, DWORD, void *, DWORD);
|
2018-07-12 00:57:50 +02:00
|
|
|
static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, void *, ULONG, ULONG *);
|
2014-10-06 22:31:43 +02:00
|
|
|
static NTSTATUS (WINAPI * pRtlGetVersion)(RTL_OSVERSIONINFOEXW *);
|
2006-06-28 18:57:37 +02:00
|
|
|
|
2014-10-06 22:31:43 +02:00
|
|
|
#define GET_PROC(func) \
|
|
|
|
p##func = (void *)GetProcAddress(hmod, #func);
|
2006-06-28 18:57:37 +02:00
|
|
|
|
2018-07-12 00:57:50 +02:00
|
|
|
/* Firmware table providers */
|
|
|
|
#define ACPI 0x41435049
|
|
|
|
#define FIRM 0x4649524D
|
|
|
|
#define RSMB 0x52534D42
|
|
|
|
|
2006-06-28 18:57:37 +02:00
|
|
|
static void init_function_pointers(void)
|
|
|
|
{
|
2014-10-06 22:31:43 +02:00
|
|
|
HMODULE hmod;
|
2006-06-28 18:57:37 +02:00
|
|
|
|
2014-10-06 22:31:43 +02:00
|
|
|
hmod = GetModuleHandleA("kernel32.dll");
|
2012-05-30 18:40:34 +02:00
|
|
|
|
2014-10-06 22:31:43 +02:00
|
|
|
GET_PROC(GetProductInfo);
|
2018-07-13 10:48:03 +02:00
|
|
|
GET_PROC(GetSystemFirmwareTable);
|
2014-10-06 22:31:43 +02:00
|
|
|
|
|
|
|
hmod = GetModuleHandleA("ntdll.dll");
|
|
|
|
|
2018-07-12 00:57:50 +02:00
|
|
|
GET_PROC(NtQuerySystemInformation);
|
2014-10-06 22:31:43 +02:00
|
|
|
GET_PROC(RtlGetVersion);
|
2006-06-28 18:57:37 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 18:40:34 +02:00
|
|
|
static void test_GetProductInfo(void)
|
|
|
|
{
|
|
|
|
DWORD product;
|
|
|
|
DWORD res;
|
|
|
|
DWORD table[] = {9,8,7,6,
|
|
|
|
7,0,0,0,
|
|
|
|
6,2,0,0,
|
|
|
|
6,1,2,0,
|
|
|
|
6,1,1,0,
|
|
|
|
6,1,0,2,
|
|
|
|
6,1,0,0,
|
|
|
|
6,0,3,0,
|
|
|
|
6,0,2,0,
|
|
|
|
6,0,1,5,
|
|
|
|
6,0,1,0,
|
|
|
|
6,0,0,0,
|
|
|
|
5,3,0,0,
|
|
|
|
5,2,0,0,
|
|
|
|
5,1,0,0,
|
|
|
|
5,0,0,0,
|
|
|
|
0};
|
|
|
|
|
|
|
|
DWORD *entry = table;
|
|
|
|
|
|
|
|
if (!pGetProductInfo)
|
|
|
|
{
|
|
|
|
/* Not present before Vista */
|
|
|
|
win_skip("GetProductInfo() not available\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*entry)
|
|
|
|
{
|
|
|
|
/* SetLastError() / GetLastError(): value is untouched */
|
|
|
|
product = 0xdeadbeef;
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
res = pGetProductInfo(entry[0], entry[1], entry[2], entry[3], &product);
|
|
|
|
|
|
|
|
if (entry[0] >= 6)
|
2018-01-02 07:34:11 +01:00
|
|
|
ok(res && (product > PRODUCT_UNDEFINED) && (product <= PRODUCT_ENTERPRISE_S_N_EVALUATION),
|
2012-06-13 17:44:09 +02:00
|
|
|
"got %d and 0x%x (expected TRUE and a valid PRODUCT_* value)\n", res, product);
|
2012-05-30 18:40:34 +02:00
|
|
|
else
|
|
|
|
ok(!res && !product && (GetLastError() == 0xdeadbeef),
|
|
|
|
"got %d and 0x%x with 0x%x (expected FALSE and PRODUCT_UNDEFINED with LastError untouched)\n",
|
|
|
|
res, product, GetLastError());
|
|
|
|
|
|
|
|
entry+= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NULL pointer is not a problem */
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
res = pGetProductInfo(6, 1, 0, 0, NULL);
|
|
|
|
ok( (!res) && (GetLastError() == 0xdeadbeef),
|
|
|
|
"got %d with 0x%x (expected FALSE with LastError untouched\n", res, GetLastError());
|
|
|
|
}
|
|
|
|
|
2007-11-30 17:52:44 +01:00
|
|
|
static void test_GetVersionEx(void)
|
|
|
|
{
|
|
|
|
OSVERSIONINFOA infoA;
|
|
|
|
OSVERSIONINFOEXA infoExA;
|
|
|
|
BOOL ret;
|
|
|
|
|
|
|
|
if (0)
|
|
|
|
{
|
|
|
|
/* Silently crashes on XP */
|
2011-02-10 22:28:07 +01:00
|
|
|
GetVersionExA(NULL);
|
2007-11-30 17:52:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
memset(&infoA,0,sizeof infoA);
|
|
|
|
ret = GetVersionExA(&infoA);
|
|
|
|
ok(!ret, "Expected GetVersionExA to fail\n");
|
2008-02-26 10:32:24 +01:00
|
|
|
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
|
|
|
|
GetLastError() == 0xdeadbeef /* Win9x */,
|
|
|
|
"Expected ERROR_INSUFFICIENT_BUFFER or 0xdeadbeef (Win9x), got %d\n",
|
|
|
|
GetLastError());
|
2007-11-30 17:52:44 +01:00
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
infoA.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA) / 2;
|
|
|
|
ret = GetVersionExA(&infoA);
|
|
|
|
ok(!ret, "Expected GetVersionExA to fail\n");
|
2008-02-26 10:32:24 +01:00
|
|
|
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
|
|
|
|
GetLastError() == 0xdeadbeef /* Win9x */,
|
|
|
|
"Expected ERROR_INSUFFICIENT_BUFFER or 0xdeadbeef (Win9x), got %d\n",
|
|
|
|
GetLastError());
|
2007-11-30 17:52:44 +01:00
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
infoA.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA) * 2;
|
|
|
|
ret = GetVersionExA(&infoA);
|
|
|
|
ok(!ret, "Expected GetVersionExA to fail\n");
|
2008-02-26 10:32:24 +01:00
|
|
|
ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
|
|
|
|
GetLastError() == 0xdeadbeef /* Win9x */,
|
|
|
|
"Expected ERROR_INSUFFICIENT_BUFFER or 0xdeadbeef (Win9x), got %d\n",
|
|
|
|
GetLastError());
|
2007-11-30 17:52:44 +01:00
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
infoA.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
|
|
|
ret = GetVersionExA(&infoA);
|
|
|
|
ok(ret, "Expected GetVersionExA to succeed\n");
|
|
|
|
ok(GetLastError() == 0xdeadbeef,
|
|
|
|
"Expected 0xdeadbeef, got %d\n", GetLastError());
|
|
|
|
|
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
infoExA.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
|
|
|
|
ret = GetVersionExA((OSVERSIONINFOA *)&infoExA);
|
2018-02-25 17:37:49 +01:00
|
|
|
ok(ret, "GetVersionExA failed.\n");
|
2016-10-04 13:28:33 +02:00
|
|
|
|
|
|
|
if (!infoExA.wServicePackMajor && !infoExA.wServicePackMinor)
|
|
|
|
ok(!infoExA.szCSDVersion[0], "got '%s'\n", infoExA.szCSDVersion);
|
2007-11-30 17:52:44 +01:00
|
|
|
}
|
|
|
|
|
2008-02-04 18:18:52 +01:00
|
|
|
static void test_VerifyVersionInfo(void)
|
2006-06-14 14:19:50 +02:00
|
|
|
{
|
2018-02-25 17:37:48 +01:00
|
|
|
enum srcversion_mode
|
|
|
|
{
|
|
|
|
SRCVERSION_ZERO = 0,
|
|
|
|
SRCVERSION_CURRENT = 1,
|
|
|
|
SRCVERSION_INC_MINOR = 2,
|
|
|
|
SRCVERSION_INC_SP_MINOR = 3,
|
|
|
|
SRCVERSION_INC_SP_MAJOR = 4,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR = 5,
|
|
|
|
SRCVERSION_DEC_MAJOR = 6,
|
|
|
|
SRCVERSION_INC_BUILD = 7,
|
|
|
|
SRCVERSION_REQUIRES_SP = 0x1000,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct verify_version_test
|
|
|
|
{
|
|
|
|
DWORD verifymask; /* Type mask for VerifyVersionInfo() */
|
|
|
|
DWORD srcinfo; /* The way current version info is modified. */
|
|
|
|
DWORD err; /* Error code on failure, 0 on success. */
|
|
|
|
|
|
|
|
DWORD typemask1;
|
|
|
|
DWORD condition1;
|
|
|
|
DWORD typemask2;
|
|
|
|
DWORD condition2;
|
|
|
|
DWORD typemask3;
|
|
|
|
DWORD condition3;
|
|
|
|
DWORD typemask4;
|
|
|
|
DWORD condition4;
|
|
|
|
|
|
|
|
BOOL todo;
|
|
|
|
} verify_version_tests[] =
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_CURRENT,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_CURRENT,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_AND,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_LESS_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_AND,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_OR,
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMINOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_LESS_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_CURRENT,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_LESS_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_AND,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION,
|
|
|
|
SRCVERSION_ZERO,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER,
|
|
|
|
SRCVERSION_ZERO,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_SUITENAME,
|
|
|
|
SRCVERSION_ZERO,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_SUITENAME, VER_AND,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_SUITENAME,
|
|
|
|
SRCVERSION_ZERO,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_SUITENAME, VER_OR,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_LESS,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_LESS_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_SP_MAJOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_MINOR,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_CURRENT,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_BUILD,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_INC_BUILD,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MINORVERSION, VER_GREATER_EQUAL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_LESS_EQUAL,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
0,
|
|
|
|
|
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL,
|
|
|
|
VER_MINORVERSION, VER_AND,
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
SRCVERSION_DEC_SP_MAJOR | SRCVERSION_REQUIRES_SP,
|
|
|
|
ERROR_OLD_WIN_VERSION,
|
|
|
|
|
|
|
|
VER_SERVICEPACKMAJOR, VER_GREATER,
|
|
|
|
VER_SERVICEPACKMINOR, VER_EQUAL,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-10-21 10:02:57 +02:00
|
|
|
OSVERSIONINFOEXA info;
|
2018-02-25 17:37:48 +01:00
|
|
|
DWORD servicepack;
|
|
|
|
unsigned int i;
|
2006-06-14 14:19:50 +02:00
|
|
|
BOOL ret;
|
2006-06-28 18:57:37 +02:00
|
|
|
|
2008-03-11 11:09:38 +01:00
|
|
|
/* Before we start doing some tests we should check what the version of
|
|
|
|
* the ServicePack is. Tests on a box with no ServicePack will fail otherwise.
|
|
|
|
*/
|
2013-10-21 10:02:57 +02:00
|
|
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
|
|
|
|
GetVersionExA((OSVERSIONINFOA *)&info);
|
2008-03-11 11:09:38 +01:00
|
|
|
servicepack = info.wServicePackMajor;
|
2018-02-25 17:37:48 +01:00
|
|
|
if (servicepack == 0)
|
|
|
|
skip("There is no ServicePack on this system. Some tests will be skipped.\n");
|
2014-10-06 22:31:43 +02:00
|
|
|
|
|
|
|
/* Win8.1+ returns Win8 version in GetVersionEx when there's no app manifest targeting 8.1 */
|
|
|
|
if (info.dwMajorVersion == 6 && info.dwMinorVersion == 2)
|
|
|
|
{
|
|
|
|
RTL_OSVERSIONINFOEXW rtlinfo;
|
|
|
|
rtlinfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
|
2018-02-25 17:37:48 +01:00
|
|
|
ok(!pRtlGetVersion(&rtlinfo), "RtlGetVersion failed\n");
|
2014-10-06 22:31:43 +02:00
|
|
|
|
|
|
|
if (rtlinfo.dwMajorVersion != 6 || rtlinfo.dwMinorVersion != 2)
|
|
|
|
{
|
|
|
|
win_skip("GetVersionEx and VerifyVersionInfo are faking values\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-27 20:56:45 +02:00
|
|
|
for (i = 0; i < ARRAY_SIZE(verify_version_tests); i++)
|
2018-02-25 17:37:48 +01:00
|
|
|
{
|
|
|
|
struct verify_version_test *test = &verify_version_tests[i];
|
|
|
|
DWORD srcinfo = test->srcinfo;
|
|
|
|
ULONGLONG mask;
|
2008-03-11 11:09:38 +01:00
|
|
|
|
2018-02-25 17:37:48 +01:00
|
|
|
if (servicepack == 0 && srcinfo & SRCVERSION_REQUIRES_SP)
|
|
|
|
continue;
|
|
|
|
srcinfo &= ~SRCVERSION_REQUIRES_SP;
|
2006-06-14 14:19:50 +02:00
|
|
|
|
2018-02-25 17:37:48 +01:00
|
|
|
info.dwOSVersionInfoSize = sizeof(info);
|
|
|
|
GetVersionExA((OSVERSIONINFOA *)&info);
|
2006-06-14 14:19:50 +02:00
|
|
|
|
2018-02-25 17:37:48 +01:00
|
|
|
switch (srcinfo)
|
|
|
|
{
|
|
|
|
case SRCVERSION_ZERO:
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
break;
|
|
|
|
case SRCVERSION_INC_MINOR:
|
|
|
|
info.dwMinorVersion++;
|
|
|
|
break;
|
|
|
|
case SRCVERSION_INC_SP_MINOR:
|
|
|
|
info.wServicePackMinor++;
|
|
|
|
break;
|
|
|
|
case SRCVERSION_INC_SP_MAJOR:
|
|
|
|
info.wServicePackMajor++;
|
|
|
|
break;
|
|
|
|
case SRCVERSION_DEC_SP_MAJOR:
|
|
|
|
info.wServicePackMajor--;
|
|
|
|
break;
|
|
|
|
case SRCVERSION_DEC_MAJOR:
|
|
|
|
info.dwMajorVersion--;
|
|
|
|
break;
|
|
|
|
case SRCVERSION_INC_BUILD:
|
|
|
|
info.dwBuildNumber++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
2006-06-14 14:19:50 +02:00
|
|
|
|
2018-02-25 17:37:48 +01:00
|
|
|
mask = VerSetConditionMask(0, test->typemask1, test->condition1);
|
|
|
|
if (test->typemask2)
|
|
|
|
mask = VerSetConditionMask(mask, test->typemask2, test->condition2);
|
|
|
|
if (test->typemask3)
|
|
|
|
mask = VerSetConditionMask(mask, test->typemask3, test->condition3);
|
|
|
|
if (test->typemask4)
|
|
|
|
mask = VerSetConditionMask(mask, test->typemask4, test->condition4);
|
2006-06-14 14:19:50 +02:00
|
|
|
|
2018-02-25 17:37:48 +01:00
|
|
|
SetLastError(0xdeadbeef);
|
|
|
|
ret = VerifyVersionInfoA(&info, test->verifymask, mask);
|
|
|
|
todo_wine_if(test->todo)
|
|
|
|
{
|
|
|
|
ok(test->err ? !ret : ret, "%u: unexpected return value %d.\n", i, ret);
|
|
|
|
if (!ret)
|
|
|
|
ok(GetLastError() == test->err, "%u: unexpected error code %d, expected %d.\n", i, GetLastError(), test->err);
|
|
|
|
}
|
|
|
|
}
|
2006-06-14 14:19:50 +02:00
|
|
|
|
|
|
|
/* test handling of version numbers */
|
|
|
|
/* v3.10 is always less than v4.x even
|
|
|
|
* if the minor version is tested */
|
|
|
|
info.dwMajorVersion = 3;
|
|
|
|
info.dwMinorVersion = 10;
|
2018-02-25 17:37:49 +01:00
|
|
|
ret = VerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
VerSetConditionMask(VerSetConditionMask(0, VER_MINORVERSION, VER_GREATER_EQUAL),
|
2006-06-14 14:19:50 +02:00
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL));
|
2006-10-10 01:06:48 +02:00
|
|
|
ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError());
|
2006-06-14 14:19:50 +02:00
|
|
|
|
|
|
|
info.dwMinorVersion = 0;
|
|
|
|
info.wServicePackMajor = 10;
|
2018-02-25 17:37:49 +01:00
|
|
|
ret = VerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
VerSetConditionMask(VerSetConditionMask(0, VER_MINORVERSION, VER_GREATER_EQUAL),
|
2006-06-14 14:19:50 +02:00
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL));
|
2006-10-10 01:06:48 +02:00
|
|
|
ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError());
|
2006-06-14 14:19:50 +02:00
|
|
|
|
|
|
|
info.wServicePackMajor = 0;
|
|
|
|
info.wServicePackMinor = 10;
|
2018-02-25 17:37:49 +01:00
|
|
|
ret = VerifyVersionInfoA(&info, VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
VerSetConditionMask(VerSetConditionMask(0, VER_MINORVERSION, VER_GREATER_EQUAL),
|
2006-06-14 14:19:50 +02:00
|
|
|
VER_MAJORVERSION, VER_GREATER_EQUAL));
|
2011-02-10 22:28:07 +01:00
|
|
|
ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError());
|
2006-06-14 14:19:50 +02:00
|
|
|
|
|
|
|
/* test bad dwOSVersionInfoSize */
|
2013-10-21 10:02:57 +02:00
|
|
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA);
|
|
|
|
GetVersionExA((OSVERSIONINFOA *)&info);
|
2006-06-14 14:19:50 +02:00
|
|
|
info.dwOSVersionInfoSize = 0;
|
2018-02-25 17:37:49 +01:00
|
|
|
ret = VerifyVersionInfoA(&info, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
|
|
|
|
VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL));
|
2018-02-25 17:37:48 +01:00
|
|
|
ok(ret, "VerifyVersionInfoA failed with error %d\n", GetLastError());
|
2006-06-14 14:19:50 +02:00
|
|
|
}
|
2008-02-04 18:18:52 +01:00
|
|
|
|
2018-07-12 00:57:50 +02:00
|
|
|
static void test_GetSystemFirmwareTable(void)
|
|
|
|
{
|
|
|
|
static const ULONG min_sfti_len = FIELD_OFFSET(SYSTEM_FIRMWARE_TABLE_INFORMATION, TableBuffer);
|
|
|
|
ULONG expected_len;
|
|
|
|
UINT len;
|
2018-07-13 10:48:03 +02:00
|
|
|
SYSTEM_FIRMWARE_TABLE_INFORMATION *sfti;
|
2018-07-12 00:57:50 +02:00
|
|
|
UCHAR *smbios_table;
|
|
|
|
|
2018-07-13 10:48:03 +02:00
|
|
|
if (!pGetSystemFirmwareTable)
|
|
|
|
{
|
|
|
|
win_skip("GetSystemFirmwareTable not available\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfti = HeapAlloc(GetProcessHeap(), 0, min_sfti_len);
|
2018-07-12 00:57:50 +02:00
|
|
|
ok(!!sfti, "Failed to allocate memory\n");
|
|
|
|
sfti->ProviderSignature = RSMB;
|
|
|
|
sfti->Action = SystemFirmwareTable_Get;
|
|
|
|
sfti->TableID = 0;
|
|
|
|
pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, min_sfti_len, &expected_len);
|
|
|
|
if (expected_len == 0) /* xp, 2003 */
|
|
|
|
{
|
2018-07-12 00:57:52 +02:00
|
|
|
win_skip("SystemFirmwareTableInformation is not available\n");
|
2018-07-12 00:57:50 +02:00
|
|
|
HeapFree(GetProcessHeap(), 0, sfti);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sfti = HeapReAlloc(GetProcessHeap(), 0, sfti, expected_len);
|
|
|
|
ok(!!sfti, "Failed to allocate memory\n");
|
|
|
|
pNtQuerySystemInformation(SystemFirmwareTableInformation, sfti, expected_len, &expected_len);
|
|
|
|
|
|
|
|
expected_len -= min_sfti_len;
|
|
|
|
smbios_table = HeapAlloc(GetProcessHeap(), 0, expected_len);
|
2018-07-13 10:48:03 +02:00
|
|
|
len = pGetSystemFirmwareTable(RSMB, 0, smbios_table, expected_len);
|
2018-07-12 00:57:50 +02:00
|
|
|
ok(len == expected_len, "Expected length %u, got %u\n", expected_len, len);
|
|
|
|
ok(len == 0 || !memcmp(smbios_table, sfti->TableBuffer, 6),
|
|
|
|
"Expected prologue %02x %02x %02x %02x %02x %02x, got %02x %02x %02x %02x %02x %02x\n",
|
|
|
|
sfti->TableBuffer[0], sfti->TableBuffer[1], sfti->TableBuffer[2],
|
|
|
|
sfti->TableBuffer[3], sfti->TableBuffer[4], sfti->TableBuffer[5],
|
|
|
|
smbios_table[0], smbios_table[1], smbios_table[2],
|
|
|
|
smbios_table[3], smbios_table[4], smbios_table[5]);
|
|
|
|
|
|
|
|
HeapFree(GetProcessHeap(), 0, sfti);
|
|
|
|
HeapFree(GetProcessHeap(), 0, smbios_table);
|
|
|
|
}
|
|
|
|
|
2008-02-04 18:18:52 +01:00
|
|
|
START_TEST(version)
|
|
|
|
{
|
|
|
|
init_function_pointers();
|
|
|
|
|
2012-05-30 18:40:34 +02:00
|
|
|
test_GetProductInfo();
|
2008-02-04 18:18:52 +01:00
|
|
|
test_GetVersionEx();
|
|
|
|
test_VerifyVersionInfo();
|
2018-07-12 00:57:50 +02:00
|
|
|
test_GetSystemFirmwareTable();
|
2008-02-04 18:18:52 +01:00
|
|
|
}
|