[UCRTBASE_WINETEST] Import ucrtbase_winetest from wine-10.0

This commit is contained in:
Timo Kreuzer 2024-10-26 12:41:54 +03:00
parent 5b633d64f1
commit 5c47607ab2
11 changed files with 5202 additions and 0 deletions

View file

@ -0,0 +1,244 @@
/*
* Copyright 2016 Daniel Lehman (Esri)
*
* 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
*/
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdio.h>
#include <windef.h>
#include <winbase.h>
#include <verrsrc.h>
#include <dbghelp.h>
#include "wine/test.h"
typedef unsigned char MSVCRT_bool;
typedef struct {
const char *what;
MSVCRT_bool dofree;
} __std_exception_data;
typedef struct
{
char *name;
char mangled[32];
} type_info140;
typedef struct _type_info_list
{
SLIST_ENTRY entry;
char name[1];
} type_info_list;
static void* (CDECL *p_malloc)(size_t);
static void (CDECL *p___std_exception_copy)(const __std_exception_data*, __std_exception_data*);
static void (CDECL *p___std_exception_destroy)(__std_exception_data*);
static int (CDECL *p___std_type_info_compare)(const type_info140*, const type_info140*);
static const char* (CDECL *p___std_type_info_name)(type_info140*, SLIST_HEADER*);
static void (CDECL *p___std_type_info_destroy_list)(SLIST_HEADER*);
static size_t (CDECL *p___std_type_info_hash)(type_info140*);
static char* (__cdecl *p___unDName)(char*,const char*,int,void*,void*,unsigned short int);
static BOOL init(void)
{
HMODULE module;
module = LoadLibraryA("ucrtbase.dll");
if (!module)
{
win_skip("ucrtbase.dll not installed\n");
return FALSE;
}
p_malloc = (void*)GetProcAddress(module, "malloc");
p___std_exception_copy = (void*)GetProcAddress(module, "__std_exception_copy");
p___std_exception_destroy = (void*)GetProcAddress(module, "__std_exception_destroy");
p___std_type_info_compare = (void*)GetProcAddress(module, "__std_type_info_compare");
p___std_type_info_name = (void*)GetProcAddress(module, "__std_type_info_name");
p___std_type_info_destroy_list = (void*)GetProcAddress(module, "__std_type_info_destroy_list");
p___std_type_info_hash = (void*)GetProcAddress(module, "__std_type_info_hash");
p___unDName = (void*)GetProcAddress(module, "__unDName");
return TRUE;
}
static void test___std_exception(void)
{
__std_exception_data src;
__std_exception_data dst;
if (0) /* crash on Windows */
{
p___std_exception_copy(NULL, &src);
p___std_exception_copy(&dst, NULL);
src.what = "invalid free";
src.dofree = 1;
p___std_exception_destroy(&src);
p___std_exception_destroy(NULL);
}
src.what = "what";
src.dofree = 0;
p___std_exception_copy(&src, &dst);
ok(dst.what == src.what, "expected what to be same, got src %p dst %p\n", src.what, dst.what);
ok(!dst.dofree, "expected 0, got %d\n", dst.dofree);
src.dofree = 0x42;
p___std_exception_copy(&src, &dst);
ok(dst.what != src.what, "expected what to be different, got src %p dst %p\n", src.what, dst.what);
ok(dst.dofree == 1, "expected 1, got %d\n", dst.dofree);
p___std_exception_destroy(&dst);
ok(!dst.what, "expected NULL, got %p\n", dst.what);
ok(!dst.dofree, "expected 0, got %d\n", dst.dofree);
src.what = NULL;
src.dofree = 0;
p___std_exception_copy(&src, &dst);
ok(!dst.what, "dst.what != NULL\n");
ok(!dst.dofree, "dst.dofree != FALSE\n");
src.what = NULL;
src.dofree = 1;
p___std_exception_copy(&src, &dst);
ok(!dst.what, "dst.what != NULL\n");
ok(!dst.dofree, "dst.dofree != FALSE\n");
}
static void test___std_type_info(void)
{
type_info140 ti1 = { NULL, ".?AVa@@" };
type_info140 ti2 = { NULL, ".?AVb@@" };
type_info140 ti3 = ti1;
SLIST_HEADER header;
type_info_list *elem;
const char *ret;
size_t hash1, hash2;
int eq;
InitializeSListHead(&header);
p___std_type_info_destroy_list(&header);
elem = p_malloc(sizeof(*elem));
memset(elem, 0, sizeof(*elem));
InterlockedPushEntrySList(&header, &elem->entry);
p___std_type_info_destroy_list(&header);
ok(!InterlockedPopEntrySList(&header), "list is not empty\n");
ret = p___std_type_info_name(&ti1, &header);
ok(!strcmp(ret, "class a"), "__std_type_info_name(&ti1) = %s\n", ret);
ok(ti1.name == ret, "ti1.name = %p, ret = %p\n", ti1.name, ret);
p___std_type_info_destroy_list(&header);
ok(!InterlockedPopEntrySList(&header), "list is not empty\n");
ok(ti1.name == ret, "ti1.name = %p, ret = %p\n", ti1.name, ret);
ti1.name = NULL;
eq = p___std_type_info_compare(&ti1, &ti1);
ok(eq == 0, "__std_type_info_compare(&ti1, &ti1) = %d\n", eq);
eq = p___std_type_info_compare(&ti1, &ti2);
ok(eq == -1, "__std_type_info_compare(&ti1, &ti2) = %d\n", eq);
eq = p___std_type_info_compare(&ti1, &ti3);
ok(eq == 0, "__std_type_info_compare(&ti1, &ti3) = %d\n", eq);
ti1.mangled[0] = 0;
ti1.mangled[1] = 0;
ti1.mangled[2] = 0;
hash1 = p___std_type_info_hash(&ti1);
#ifdef _WIN64
ok(hash1 == 0xcbf29ce44fd0bfc1, "hash = %p\n", (void*)hash1);
#else
ok(hash1 == 0x811c9dc5, "hash = %p\n", (void*)hash1);
#endif
ti1.mangled[0] = 1;
hash2 = p___std_type_info_hash(&ti1);
ok(hash1 == hash2, "hash1 != hash2 (first char not ignored)\n");
ti1.mangled[1] = 1;
hash1 = p___std_type_info_hash(&ti1);
#ifdef _WIN64
ok(hash1 == 0xaf63bc4c29620a60, "hash = %p\n", (void*)hash1);
#else
ok(hash1 == 0x40c5b8c, "hash = %p\n", (void*)hash1);
#endif
ok(hash1 != hash2, "hash1 == hash2 for different strings\n");
ti1.mangled[1] = 2;
hash2 = p___std_type_info_hash(&ti1);
ok(hash1 != hash2, "hash1 == hash2 for different strings\n");
hash1 = p___std_type_info_hash(&ti2);
ok(hash1 != hash2, "hash1 == hash2 for different strings\n");
}
static void test___unDName(void)
{
static struct {const char *in; const char *out; const char *broken; unsigned int flags;} und_tests[] =
{
/* 0 */ {"??4QDnsDomainNameRecord@@QAEAAV0@$$QAV0@@Z",
"public: class QDnsDomainNameRecord & __thiscall QDnsDomainNameRecord::operator=(class QDnsDomainNameRecord &&)"},
/* 1 */ {"??4QDnsDomainNameRecord@@QAEAAV0@$$QEAV0@@Z",
"public: class QDnsDomainNameRecord & __thiscall QDnsDomainNameRecord::operator=(class QDnsDomainNameRecord && __ptr64)"},
/* 2 */ {"??__K_l@@YA?AUCC@@I@Z", "struct CC __cdecl operator \"\" _l(unsigned int)",
"??__K_l@@YA?AUCC@@I@Z" /* W10 1507 fails on this :-( */},
/* 3 */ {"?meth@Q@@QEGBA?AV1@XZ",
"public: class Q __cdecl Q::meth(void)const __ptr64& ",
"public: ?? :: ?? ::XZ::V1" /* W10 1507 fails on this :-( */},
/* 4 */ {"?meth@Q@@QEHAA?AV1@XZ",
"public: class Q __cdecl Q::meth(void) __ptr64&& ",
"public: ?? :: ?? ::XZ::V1" /* W10 1507 fails on this :-( */},
/* 5 */ {"?meth@Q@@QEGBA?AV1@XZ",
"public: class Q Q::meth(void)const & ",
"public: ?? :: ?? ::XZ::V1" /* W10 1507 fails on this :-( */,
UNDNAME_NO_MS_KEYWORDS},
/* 6 */ {"?meth@Q@@QEHAA?AV1@XZ",
"public: class Q Q::meth(void)&& ",
"public: ?? :: ?? ::XZ::V1" /* W10 1507 fails on this :-( */,
UNDNAME_NO_MS_KEYWORDS},
/* 7 */ {"?AU?$my_iter@H$0A@$$V@@",
"struct my_iter<int,0>",
NULL,
UNDNAME_NO_ARGUMENTS},
/* 8 */ {"??$foo@J_W$$T@bar@@YAJQB_W$$THQAUgod@@@Z",
"long __cdecl bar::foo<long,wchar_t,std::nullptr_t>(wchar_t const * const,std::nullptr_t,int,struct god * const)"},
};
unsigned i;
for (i = 0; i < ARRAY_SIZE(und_tests); i++)
{
char *name = p___unDName(0, und_tests[i].in, 0, malloc, free, und_tests[i].flags);
ok(!strcmp(name, und_tests[i].out) ||
broken(und_tests[i].broken && !strcmp(und_tests[i].broken, name)),
"unDName returned %s for #%u\n", wine_dbgstr_a(name), i);
free(name);
}
}
START_TEST(cpp)
{
if (!init()) return;
test___std_exception();
test___std_type_info();
test___unDName();
}

View file

@ -0,0 +1,344 @@
/*
* Unit tests for C library environment routines
*
* Copyright 2004 Mike Hearn <mh@codeweavers.com>
*
* 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
*/
#include "wine/test.h"
#include <errno.h>
#include <stdlib.h>
#include <process.h>
#include <winnls.h>
#define DEFINE_EXPECT(func) \
static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
#define SET_EXPECT(func) \
expect_ ## func = TRUE
#define CHECK_EXPECT2(func) \
do { \
ok(expect_ ##func, "unexpected call " #func "\n"); \
called_ ## func = TRUE; \
}while(0)
#define CHECK_EXPECT(func) \
do { \
CHECK_EXPECT2(func); \
expect_ ## func = FALSE; \
}while(0)
#define CHECK_CALLED(func) \
do { \
ok(called_ ## func, "expected " #func "\n"); \
expect_ ## func = called_ ## func = FALSE; \
}while(0)
DEFINE_EXPECT(invalid_parameter_handler);
static const char *a_very_long_env_string =
"LIBRARY_PATH="
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/;"
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/;"
"/mingw/lib/gcc/mingw32/3.4.2/;"
"/usr/lib/gcc/mingw32/3.4.2/;"
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../../mingw32/lib/mingw32/3.4.2/;"
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../../mingw32/lib/;"
"/mingw/mingw32/lib/mingw32/3.4.2/;"
"/mingw/mingw32/lib/;"
"/mingw/lib/mingw32/3.4.2/;"
"/mingw/lib/;"
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../mingw32/3.4.2/;"
"C:/Program Files/GLBasic/Compiler/platform/Win32/Bin/../lib/gcc/mingw32/3.4.2/../../../;"
"/mingw/lib/mingw32/3.4.2/;"
"/mingw/lib/;"
"/lib/mingw32/3.4.2/;"
"/lib/;"
"/usr/lib/mingw32/3.4.2/;"
"/usr/lib/";
static char ***(__cdecl *p__p__environ)(void);
static WCHAR ***(__cdecl *p__p__wenviron)(void);
static char** (__cdecl *p_get_initial_narrow_environment)(void);
static wchar_t** (__cdecl *p_get_initial_wide_environment)(void);
static errno_t (__cdecl *p_putenv_s)(const char*, const char*);
static errno_t (__cdecl *p_wputenv_s)(const wchar_t*, const wchar_t*);
static errno_t (__cdecl *p_getenv_s)(size_t*, char*, size_t, const char*);
static char ***p_environ;
static WCHAR ***p_wenviron;
static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
const wchar_t *function, const wchar_t *file,
unsigned line, uintptr_t arg)
{
CHECK_EXPECT(invalid_parameter_handler);
ok(expression == NULL, "expression is not NULL\n");
ok(function == NULL, "function is not NULL\n");
ok(file == NULL, "file is not NULL\n");
ok(line == 0, "line = %u\n", line);
ok(arg == 0, "arg = %Ix\n", arg);
}
static BOOL init(void)
{
HMODULE hmod = GetModuleHandleA( "ucrtbase.dll" );
p__p__environ = (void *)GetProcAddress( hmod, "__p__environ" );
p__p__wenviron = (void *)GetProcAddress( hmod, "__p__wenviron" );
p_get_initial_narrow_environment = (void *)GetProcAddress( hmod, "_get_initial_narrow_environment" );
p_get_initial_wide_environment = (void *)GetProcAddress( hmod, "_get_initial_wide_environment" );
p_putenv_s = (void *)GetProcAddress( hmod, "_putenv_s" );
p_wputenv_s = (void *)GetProcAddress( hmod, "_wputenv_s" );
p_getenv_s = (void *)GetProcAddress( hmod, "getenv_s" );
ok(p__p__environ != NULL, "Unexecped NULL pointer to environ\n" );
ok(p__p__wenviron != NULL, "Unexecped NULL pointer to environ\n" );
if (!p__p__environ || !p__p__wenviron)
{
skip( "NULL pointers for environment\n" );
return FALSE;
}
p_environ = p__p__environ();
p_wenviron = p__p__wenviron();
return TRUE;
}
static unsigned env_get_entry_countA( char **env )
{
unsigned count;
if (!env) return 0;
for (count = 0; env[count] != NULL; count++) {}
return count;
}
static void test_initial_environ( void )
{
ok( p__p__environ() != NULL, "Unexpected NULL _environ[]\n" );
ok( *p__p__environ() != NULL, "Unexpected empty _environ[]\n" );
ok( p_get_initial_narrow_environment() != NULL, "Unexpected empty narrow initial environment\n" );
ok( p_get_initial_narrow_environment() == *p__p__environ(), "Expecting _environ[] to match initial narrow environment\n" );
ok( p__p__wenviron() != NULL, "Unexpected NULL _wenviron[]\n" );
ok( *p__p__wenviron() == NULL, "Unexpected non empty _wenviron[]\n" );
ok( p_get_initial_wide_environment() != NULL, "Unexpected empty wide initial environment\n" );
ok( p_get_initial_wide_environment() == *p__p__wenviron(), "Expecting _wenviron[] to match initial wide environment\n" );
}
static void test_environment_manipulation(void)
{
char buf[256];
errno_t ret;
size_t len;
unsigned count;
char* first;
char* second;
ok( _putenv( "cat=" ) == 0, "_putenv failed on deletion of nonexistent environment variable\n" );
ok( _putenv( "cat=dog" ) == 0, "failed setting cat=dog\n" );
ok( strcmp( getenv( "cat" ), "dog" ) == 0, "getenv did not return 'dog'\n" );
if (p_getenv_s)
{
ret = p_getenv_s( &len, buf, sizeof(buf), "cat" );
ok( !ret, "getenv_s returned %d\n", ret );
ok( len == 4, "getenv_s returned length is %Id\n", len);
ok( !strcmp(buf, "dog"), "getenv_s did not return 'dog'\n" );
}
ok( _putenv("cat=") == 0, "failed deleting cat\n" );
ok( _putenv("=") == -1, "should not accept '=' as input\n" );
ok( _putenv("=dog") == -1, "should not accept '=dog' as input\n" );
ok( _putenv(a_very_long_env_string) == 0, "_putenv failed for long environment string\n" );
ok( getenv("nonexistent") == NULL, "getenv should fail with nonexistent var name\n" );
if (p_putenv_s)
{
SET_EXPECT(invalid_parameter_handler);
ret = p_putenv_s( NULL, "dog" );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
SET_EXPECT(invalid_parameter_handler);
ret = p_putenv_s( "cat", NULL );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
SET_EXPECT(invalid_parameter_handler);
ret = p_putenv_s( "a=b", NULL );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_putenv_s returned %d\n", ret );
ret = p_putenv_s( "cat", "a=b" );
ok( !ret, "_putenv_s returned %d\n", ret );
ret = p_putenv_s( "cat", "" );
ok( !ret, "_putenv_s returned %d\n", ret );
}
if (p_wputenv_s)
{
SET_EXPECT(invalid_parameter_handler);
ret = p_wputenv_s( NULL, L"dog" );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
SET_EXPECT(invalid_parameter_handler);
ret = p_wputenv_s( L"cat", NULL );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
SET_EXPECT(invalid_parameter_handler);
ret = p_wputenv_s( L"a=b", NULL );
CHECK_CALLED(invalid_parameter_handler);
ok( ret == EINVAL, "_wputenv_s returned %d\n", ret );
ret = p_wputenv_s( L"cat", L"a=b" );
ok( !ret, "_wputenv_s returned %d\n", ret );
ret = p_wputenv_s( L"cat", L"" );
ok( !ret, "_wputenv_s returned %d\n", ret );
}
if (p_getenv_s)
{
buf[0] = 'x';
len = 1;
errno = 0xdeadbeef;
ret = p_getenv_s( &len, buf, sizeof(buf), "nonexistent" );
ok( !ret, "_getenv_s returned %d\n", ret );
ok( !len, "getenv_s returned length is %Id\n", len );
ok( !buf[0], "buf = %s\n", buf );
ok( errno == 0xdeadbeef, "errno = %d\n", errno );
buf[0] = 'x';
len = 1;
errno = 0xdeadbeef;
ret = p_getenv_s( &len, buf, sizeof(buf), NULL );
ok( !ret, "_getenv_s returned %d\n", ret );
ok( !len, "getenv_s returned length is %Id\n", len );
ok( !buf[0], "buf = %s\n", buf );
ok( errno == 0xdeadbeef, "errno = %d\n", errno );
}
/* test stability of _environ[] pointers */
ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" );
ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
count = env_get_entry_countA( *p_environ );
ok( _putenv( "__winetest_cat=mew") == 0, "Couldn't set env var\n" );
ok( !strcmp( (*p_environ)[count], "__winetest_cat=mew"), "Unexpected env var value\n" );
first = (*p_environ)[count];
ok( getenv("__winetest_cat") == strchr( (*p_environ)[count], '=') + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" );
ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Unexpected env var value\n" );
ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
ok( first == (*p_environ)[count], "Expected stability of _environ[count] pointer\n" );
second = (*p_environ)[count + 1];
ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
ok( _putenv( "__winetest_cat=purr" ) == 0, "Couldn't set env var\n" );
ok( !strcmp( (*p_environ)[count], "__winetest_cat=purr" ), "Unexpected env var value\n" );
ok( getenv( "__winetest_cat" ) == strchr( (*p_environ)[count], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
ok( second == (*p_environ)[count + 1], "Expected stability of _environ[count] pointer\n" );
ok( !strcmp( (*p_environ)[count + 1], "__winetest_dog=bark" ), "Couldn't get env var value\n" );
ok( getenv( "__winetest_dog" ) == strchr( (*p_environ)[count + 1], '=' ) + 1, "Expected getenv() to return pointer inside _environ[] entry\n" );
ok( count + 2 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env vat\n" );
ok( second == (*p_environ)[count], "Expected _environ[count] to be second\n" );
ok( !strcmp( (*p_environ)[count], "__winetest_dog=bark" ), "Unexpected env var value\n" );
ok( count + 1 == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
ok( count == env_get_entry_countA( *p_environ ), "Unexpected count\n" );
/* in putenv, only changed variable is updated (no other reload of kernel info is done) */
ret = SetEnvironmentVariableA( "__winetest_cat", "meow" );
ok( ret, "SetEnvironmentVariableA failed: %lu\n", GetLastError() );
ok( _putenv( "__winetest_dog=bark" ) == 0, "Couldn't set env var\n" );
ok( getenv( "__winetest_cat" ) == NULL, "msvcrt env cache shouldn't have been updated\n" );
ok( _putenv( "__winetest_cat=" ) == 0, "Couldn't reset env var\n" );
ok( _putenv( "__winetest_dog=" ) == 0, "Couldn't reset env var\n" );
/* test setting unicode bits */
count = env_get_entry_countA( *p_environ );
ret = WideCharToMultiByte( CP_ACP, 0, L"\u263a", -1, buf, ARRAY_SIZE(buf), 0, 0 );
ok( ret, "WideCharToMultiByte failed: %lu\n", GetLastError() );
ok( _wputenv( L"__winetest_cat=\u263a" ) == 0, "Couldn't set env var\n" );
ok( _wgetenv( L"__winetest_cat" ) && !wcscmp( _wgetenv( L"__winetest_cat" ), L"\u263a" ), "Couldn't retrieve env var\n" );
ok( getenv( "__winetest_cat" ) && !strcmp( getenv( "__winetest_cat" ), buf ), "Couldn't retrieve env var\n" );
ok( _wputenv( L"__winetest_cat=" ) == 0, "Couldn't reset env var\n" );
ret = WideCharToMultiByte( CP_ACP, 0, L"__winetest_\u263a", -1, buf, ARRAY_SIZE(buf), 0, 0 );
ok( ret, "WideCharToMultiByte failed: %lu\n", GetLastError() );
ok( _wputenv( L"__winetest_\u263a=bark" ) == 0, "Couldn't set env var\n" );
ok( _wgetenv( L"__winetest_\u263a" ) && !wcscmp( _wgetenv( L"__winetest_\u263a" ), L"bark"), "Couldn't retrieve env var\n" );
ok( getenv( buf ) && !strcmp( getenv( buf ), "bark"), "Couldn't retrieve env var %s\n", wine_dbgstr_a(buf) );
ok( _wputenv( L"__winetest_\u263a=" ) == 0, "Couldn't reset env var\n" );
ok( count == env_get_entry_countA( *p_environ ), "Unexpected modification of _environ[]\n" );
}
static void test_child_env(char** argv)
{
STARTUPINFOA si = {sizeof(si)};
WCHAR *cur_env, *env, *p, *q;
PROCESS_INFORMATION pi;
char tmp[1024];
BOOL ret;
int len;
cur_env = GetEnvironmentStringsW();
ok( cur_env != NULL, "GetEnvironemntStrings failed\n" );
p = cur_env;
while (*p) p += wcslen( p ) + 1;
len = p - cur_env;
env = malloc( (len + 1024) * sizeof(*env) );
memcpy(env, cur_env, len * sizeof(*env) );
q = env + len;
FreeEnvironmentStringsW( cur_env );
wcscpy( q, L"__winetest_dog=bark" );
q += wcslen( L"__winetest_dog=bark" ) + 1;
wcscpy( q, L"__winetest_\u263a=\u03b2" );
q += wcslen( L"__winetest_\u263a=\u03b2" ) + 1;
*q = 0;
snprintf( tmp, sizeof(tmp), "%s %s create", argv[0], argv[1] );
ret = CreateProcessA( NULL, tmp, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi );
ok( ret, "Couldn't create child process %s\n", tmp );
winetest_wait_child_process( pi.hProcess );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
free( env );
}
START_TEST(environ)
{
char **argv;
int argc;
if (!init()) return;
ok( _set_invalid_parameter_handler( test_invalid_parameter_handler ) == NULL,
"Invalid parameter handler was already set\n" );
argc = winetest_get_mainargs( &argv );
if (argc == 3 && !strcmp( argv[2], "create" ))
{
ok( getenv( "__winetest_dog" ) && !strcmp( getenv( "__winetest_dog" ), "bark" ),
"Couldn't find env var\n" );
ok( _wgetenv( L"__winetest_\u263a" ) && !wcscmp( _wgetenv( L"__winetest_\u263a" ), L"\u03b2" ),
"Couldn't find unicode env var\n" );
return;
}
test_initial_environ();
test_environment_manipulation();
test_child_env(argv);
}

View file

@ -0,0 +1,472 @@
/*
* Unit test suite for file functions
*
* Copyright 2024 Eric Pouech for CodeWeavers
*
* 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
*/
#include <errno.h>
#include <direct.h>
#include <stdarg.h>
#include <locale.h>
#include <process.h>
#include <share.h>
#include <sys/stat.h>
#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include "wine/test.h"
static void test_std_stream_buffering(void)
{
int dup_fd, ret, pos;
FILE *file;
char ch;
dup_fd = _dup(STDOUT_FILENO);
ok(dup_fd != -1, "_dup failed\n");
file = freopen("std_stream_test.tmp", "w", stdout);
ok(file != NULL, "freopen failed\n");
ret = fprintf(stdout, "test");
pos = _telli64(STDOUT_FILENO);
fflush(stdout);
_dup2(dup_fd, STDOUT_FILENO);
close(dup_fd);
setvbuf(stdout, NULL, _IONBF, 0);
ok(ret == 4, "fprintf(stdout) returned %d\n", ret);
ok(!pos, "expected stdout to be buffered\n");
dup_fd = _dup(STDERR_FILENO);
ok(dup_fd != -1, "_dup failed\n");
file = freopen("std_stream_test.tmp", "w", stderr);
ok(file != NULL, "freopen failed\n");
ret = fprintf(stderr, "test");
ok(ret == 4, "fprintf(stderr) returned %d\n", ret);
pos = _telli64(STDERR_FILENO);
if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos))
trace("stderr is buffered\n");
else
ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos);
fflush(stderr);
_dup2(dup_fd, STDERR_FILENO);
close(dup_fd);
dup_fd = _dup(STDIN_FILENO);
ok(dup_fd != -1, "_dup failed\n");
file = freopen("std_stream_test.tmp", "r", stdin);
ok(file != NULL, "freopen failed\n");
ch = 0;
ret = fscanf(stdin, "%c", &ch);
ok(ret == 1, "fscanf returned %d\n", ret);
ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch);
pos = _telli64(STDIN_FILENO);
ok(pos == 4, "pos = %d\n", pos);
fflush(stdin);
_dup2(dup_fd, STDIN_FILENO);
close(dup_fd);
ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n");
}
int CDECL _get_stream_buffer_pointers(FILE*,char***,char***,int**);
static void test_iobuf_layout(void)
{
union
{
FILE *f;
struct
{
char* _ptr;
char* _base;
int _cnt;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
CRITICAL_SECTION _crit;
} *iobuf;
} fp;
char *tempf, *ptr, **file_ptr, **file_base;
int cnt, r, *file_cnt;
tempf = _tempnam(".","wne");
fp.f = fopen(tempf, "wb");
ok(fp.f != NULL, "fopen failed with error: %d\n", errno);
ok(!(fp.iobuf->_flag & 0x440), "fp.iobuf->_flag = %x\n", fp.iobuf->_flag);
r = fprintf(fp.f, "%s", "init");
ok(r == 4, "fprintf returned %d\n", r);
ok(fp.iobuf->_flag & 0x40, "fp.iobuf->_flag = %x\n", fp.iobuf->_flag);
ok(fp.iobuf->_cnt + 4 == fp.iobuf->_bufsiz, "_cnt = %d, _bufsiz = %d\n",
fp.iobuf->_cnt, fp.iobuf->_bufsiz);
ptr = fp.iobuf->_ptr;
cnt = fp.iobuf->_cnt;
r = fprintf(fp.f, "%s", "hello");
ok(r == 5, "fprintf returned %d\n", r);
ok(ptr + 5 == fp.iobuf->_ptr, "fp.iobuf->_ptr = %p, expected %p\n", fp.iobuf->_ptr, ptr + 5);
ok(cnt - 5 == fp.iobuf->_cnt, "fp.iobuf->_cnt = %d, expected %d\n", fp.iobuf->_cnt, cnt - 5);
ok(fp.iobuf->_ptr + fp.iobuf->_cnt == fp.iobuf->_base + fp.iobuf->_bufsiz,
"_ptr = %p, _cnt = %d, _base = %p, _bufsiz = %d\n",
fp.iobuf->_ptr, fp.iobuf->_cnt, fp.iobuf->_base, fp.iobuf->_bufsiz);
_get_stream_buffer_pointers(fp.f, &file_base, &file_ptr, &file_cnt);
ok(file_base == &fp.iobuf->_base, "_base = %p, expected %p\n", file_base, &fp.iobuf->_base);
ok(file_ptr == &fp.iobuf->_ptr, "_ptr = %p, expected %p\n", file_ptr, &fp.iobuf->_ptr);
ok(file_cnt == &fp.iobuf->_cnt, "_cnt = %p, expected %p\n", file_cnt, &fp.iobuf->_cnt);
r = setvbuf(fp.f, NULL, _IONBF, 0);
ok(!r, "setvbuf returned %d\n", r);
ok(fp.iobuf->_flag & 0x400, "fp.iobuf->_flag = %x\n", fp.iobuf->_flag);
ok(TryEnterCriticalSection(&fp.iobuf->_crit), "TryEnterCriticalSection section returned FALSE\n");
LeaveCriticalSection(&fp.iobuf->_crit);
fclose(fp.f);
unlink(tempf);
}
static void test_std_stream_open(void)
{
FILE *f;
int fd;
fd = _dup(STDIN_FILENO);
ok(fd != -1, "_dup failed\n");
ok(!fclose(stdin), "fclose failed\n");
f = fopen("nul", "r");
ok(f != stdin, "f = %p, stdin = %p\n", f, stdin);
ok(_fileno(f) == STDIN_FILENO, "_fileno(f) = %d\n", _fileno(f));
ok(!fclose(f), "fclose failed\n");
f = freopen("nul", "r", stdin);
ok(f == stdin, "f = %p, expected %p\n", f, stdin);
ok(_fileno(f) == STDIN_FILENO, "_fileno(f) = %d\n", _fileno(f));
_dup2(fd, STDIN_FILENO);
close(fd);
}
static void test_fopen(void)
{
int i;
FILE *f;
wchar_t wpath[MAX_PATH];
static const struct {
const char *loc;
const char *path;
} tests[] = {
{ "German.utf8", "t\xc3\xa4\xc3\x8f\xc3\xb6\xc3\x9f.txt" },
{ "Polish.utf8", "t\xc4\x99\xc5\x9b\xc4\x87.txt" },
{ "Turkish.utf8", "t\xc3\x87\xc4\x9e\xc4\xb1\xc4\xb0\xc5\x9e.txt" },
{ "Arabic.utf8", "t\xd8\xaa\xda\x86.txt" },
{ "Japanese.utf8", "t\xe3\x82\xaf\xe3\x83\xa4.txt" },
{ "Chinese.utf8", "t\xe4\xb8\x82\xe9\xbd\xab.txt" },
{ "Japanese", "t\xb8\xd5.txt" },
};
for(i=0; i<ARRAY_SIZE(tests); i++) {
if(!setlocale(LC_ALL, tests[i].loc)) {
win_skip("skipping locale %s\n", tests[i].loc);
continue;
}
if(!MultiByteToWideChar(___lc_codepage_func() == CP_UTF8 ? CP_UTF8 : CP_ACP,
MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, tests[i].path, -1, wpath, MAX_PATH))
continue;
f = _fsopen(tests[i].path, "w", SH_DENYNO);
ok(!!f, "failed to create %s with locale %s\n",
debugstr_a(tests[i].path), tests[i].loc);
fclose(f);
f = _wfsopen(wpath, L"r", SH_DENYNO);
ok(!!f, "failed to open %s with locale %s\n",
debugstr_a(tests[i].path), tests[i].loc);
if(f) fclose(f);
ok(!unlink(tests[i].path), "failed to unlink %s with locale %s\n",
tests[i].path, tests[i].loc);
}
setlocale(LC_ALL, "C");
}
static void test_utf8(const char *argv0)
{
const char file[] = "file\xc4\x99\xc5\x9b\xc4\x87.a";
const char dir[] = "dir\xc4\x99\xc5\x9b\xc4\x87";
const WCHAR fileW[] = L"file\x0119\x015b\x0107.a";
const WCHAR dirW[] = L"dir\x0119\x015b\x0107";
char file2[32], buf[256], *p, *q, *env[2];
struct _finddata64i32_t fdata64i32;
struct _finddata32_t fdata32;
struct _finddata64_t fdata64;
intptr_t hfind, hproc;
WCHAR bufW[256], *pW;
struct _stat64 stat;
FILE *f;
int ret;
if (!setlocale(LC_ALL, ".utf8"))
{
win_skip("utf-8 tests\n");
return;
}
ret = _mkdir(dir);
if (ret == -1 && errno == ENOENT)
{
skip("can't create test environment\n");
return;
}
ok(!ret, "_mkdir returned %d, error %d\n", ret, errno);
ret = _chdir(dir);
ok(!ret, "_chdir returned %d, error %d\n", ret, errno);
p = _getcwd(buf, sizeof(buf));
ok(p == buf, "_getcwd returned %p, errno %d\n", p, errno);
p = strrchr(p, '\\');
ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf));
ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf));
p = _getdcwd(_getdrive(), buf, sizeof(buf));
ok(p == buf, "_getdcwd returned %p, errno %d\n", p, errno);
p = strrchr(p, '\\');
ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf));
ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf));
p = _fullpath(buf, NULL, sizeof(buf));
ok(p == buf, "_fulpath returned %p, errno %d\n", p, errno);
p = strrchr(p, '\\');
ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf));
ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf));
f = fopen(file, "w");
ok(!!f, "fopen returned %d, error %d\n", ret, errno);
fclose(f);
ret = access(file, 0);
ok(!ret, "access returned %d, error %d\n", ret, errno);
ret = _stat64(file, &stat);
ok(!ret, "_stat64 returned %d, error %d\n", ret, errno);
ret = _chmod(file, _S_IREAD | _S_IWRITE);
ok(!ret, "_chmod returned %d, error %d\n", ret, errno);
strcpy(file2, file);
strcat(file2, "XXXXXX");
p = _mktemp(file2);
ok(p == file2, "_mktemp returned %p, file2 %p, errno %d\n", p, file2, errno);
ok(!memcmp(file2, file, sizeof(file) - 1), "file2 = %s\n", debugstr_a(file2));
ok(p[ARRAY_SIZE(file) - 1] == 'a', "p = %s\n", debugstr_a(p));
f = fopen(p, "w");
ok(!!f, "fopen returned %d, error %d\n", ret, errno);
fclose(f);
strcpy(buf, file);
strcat(buf, "XXXXXX");
p = _mktemp(buf);
ok(p == buf, "_mktemp returned %p, buf %p, errno %d\n", p, buf, errno);
ok(!memcmp(buf, file, sizeof(file) - 1), "buf = %s\n", debugstr_a(buf));
ok(p[ARRAY_SIZE(file) - 1] == 'b', "p = %s\n", debugstr_a(p));
strcpy(buf, file);
strcat(buf, "XXXXXX");
ret = _mktemp_s(buf, sizeof(buf));
ok(!memcmp(buf, file, sizeof(file) - 1), "buf = %s\n", debugstr_a(buf));
ok(buf[ARRAY_SIZE(file) - 1] == 'b', "buf = %s\n", debugstr_a(buf));
strcpy(buf, file);
strcat(buf, "*");
fdata32.name[0] = 'x';
hfind = _findfirst32(buf, &fdata32);
ok(hfind != -1, "_findfirst32 returned %Id, errno %d\n", hfind, errno);
ok(!memcmp(file, fdata32.name, sizeof(file) - 1), "fdata32.name = %s\n", debugstr_a(fdata32.name));
fdata32.name[0] = 'x';
ret = _findnext32(hfind, &fdata32);
ok(!ret, "_findnext32 returned %d, errno %d\n", ret, errno);
ok(!memcmp(file, fdata32.name, sizeof(file) - 1), "fdata32.name = %s\n", debugstr_a(fdata32.name));
ret = _findclose(hfind);
ok(!ret, "_findclose returned %d, errno %d\n", ret, errno);
strcpy(buf, file);
strcat(buf, "*");
fdata64.name[0] = 'x';
hfind = _findfirst64(buf, &fdata64);
ok(hfind != -1, "_findfirst64 returned %Id, errno %d\n", hfind, errno);
ok(!memcmp(file, fdata64.name, sizeof(file) - 1), "fdata64.name = %s\n", debugstr_a(fdata64.name));
fdata64.name[0] = 'x';
ret = _findnext64(hfind, &fdata64);
ok(!ret, "_findnext64 returned %d, errno %d\n", ret, errno);
ok(!memcmp(file, fdata64.name, sizeof(file) - 1), "fdata64.name = %s\n", debugstr_a(fdata64.name));
ret = _findclose(hfind);
ok(!ret, "_findclose returned %d, errno %d\n", ret, errno);
strcpy(buf, file);
strcat(buf, "*");
fdata64i32.name[0] = 'x';
hfind = _findfirst64i32(buf, &fdata64i32);
ok(hfind != -1, "_findfirst64i32 returned %Id, errno %d\n", hfind, errno);
ok(!memcmp(file, fdata64i32.name, sizeof(file) - 1), "fdata64i32.name = %s\n", debugstr_a(fdata64i32.name));
fdata64i32.name[0] = 'x';
ret = _findnext64i32(hfind, &fdata64i32);
ok(!ret, "_findnext64i32 returned %d, errno %d\n", ret, errno);
ok(!memcmp(file, fdata64i32.name, sizeof(file) - 1), "fdata64i32.name = %s\n", debugstr_a(fdata64i32.name));
ret = _findclose(hfind);
ok(!ret, "_findclose returned %d, errno %d\n", ret, errno);
ret = remove(file2);
ok(!ret, "remove returned %d, errno %d\n", ret, errno);
buf[0] = 'x';
_searchenv(file, "env", buf);
p = strrchr(buf, '\\');
ok(!!p, "buf = %s\n", debugstr_a(buf));
ok(!strcmp(p + 1, file), "buf = %s\n", debugstr_a(buf));
ret = _wunlink(fileW);
ok(!ret, "_wunlink returned %d, errno %d\n", ret, errno);
ret = _chdir("..");
ok(!ret, "_chdir returned %d, error %d\n", ret, errno);
ret = _wrmdir(dirW);
ok(!ret, "_wrmdir returned %d, errno %d\n", ret, errno);
p = _tempnam(NULL, file);
ok(!!p, "_tempnam returned NULL, error %d\n", errno);
q = strrchr(p, '\\');
ok(!!q, "_tempnam returned %s\n", debugstr_a(p));
todo_wine ok(!memcmp(q + 1, file, ARRAY_SIZE(file) - 1),
"incorrect file prefix: %s\n", debugstr_a(p));
free(p);
/* native implementation mixes CP_UTF8 and CP_ACP */
if (GetACP() != CP_UTF8)
{
/* make sure wide environment is initialized (works around bug in native) */
ret = _putenv("__wine_env_test=test");
ok(!ret, "_putenv returned %d, errno %d\n", ret, errno);
_wgetenv(L"__wine_env_test");
strcpy(buf, file);
strcat(buf, "=test");
ret = _putenv(buf);
ok(!ret, "_putenv returned %d, errno %d\n", ret, errno);
/* bug in native _wgetenv/_putenv implementation */
pW = _wgetenv(fileW);
ok(!pW, "environment variable name was converted\n");
bufW[0] = 0;
ret = GetEnvironmentVariableW(fileW, bufW, ARRAY_SIZE(bufW));
todo_wine ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError());
todo_wine ok(!wcscmp(bufW, L"test"), "bufW = %s\n", debugstr_w(bufW));
strcpy(buf, file);
strcat(buf, "=");
ret = _putenv(buf);
ok(!ret, "_putenv returned %d, errno %d\n", ret, errno);
strcpy(buf, "__wine_env_test=");
strcat(buf, file);
ret = _putenv(buf);
ok(!ret, "_putenv returned %d, errno %d\n", ret, errno);
/* bug in native _wgetenv/_putenv implementation */
pW = _wgetenv(L"__wine_env_test");
ok(wcscmp(pW, fileW), "pW = %s\n", debugstr_w(pW));
ret = GetEnvironmentVariableW(L"__wine_env_test", bufW, ARRAY_SIZE(bufW));
ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError());
todo_wine ok(!wcscmp(bufW, fileW), "bufW = %s\n", debugstr_w(bufW));
wcscpy(bufW, L"__wine_env_test=");
wcscat(bufW, fileW);
ret = _wputenv(bufW);
ok(!ret, "_wputenv returned %d, errno %d\n", ret, errno);
p = getenv("__wine_env_test");
ok(strcmp(p, file), "environment variable was converted\n");
strcpy(buf, "__wine_env_test=");
ret = _putenv(buf);
ok(!ret, "_putenv returned %d, errno %d\n", ret, errno);
}
strcpy(buf, "__wine_env_test=");
strcat(buf, file);
env[0] = buf;
env[1] = NULL;
hproc = _spawnle(_P_NOWAIT, argv0, argv0, "file", "utf8", file, NULL, env);
ok(hproc != -1, "_spawnl returned %Id, errno %d\n", hproc, errno);
wait_child_process((HANDLE)hproc);
CloseHandle((HANDLE)hproc);
setlocale(LC_ALL, "C");
}
static void test_utf8_argument(void)
{
static const WCHAR nameW[] = L"file\x0119\x015b\x0107.a";
const WCHAR *cmdline = GetCommandLineW(), *p;
WCHAR buf[256];
DWORD ret;
p = wcsrchr(cmdline, ' ');
ok(!!p, "cmdline = %s\n", debugstr_w(cmdline));
ok(!wcscmp(p + 1, nameW), "cmdline = %s\n", debugstr_w(cmdline));
ret = GetEnvironmentVariableW(L"__wine_env_test", buf, ARRAY_SIZE(buf));
ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError());
if (GetACP() == CP_UTF8)
ok(!wcscmp(buf, nameW), "__wine_env_test = %s\n", debugstr_w(buf));
else
ok(wcscmp(buf, nameW), "environment was converted\n");
}
START_TEST(file)
{
int arg_c;
char** arg_v;
arg_c = winetest_get_mainargs(&arg_v);
if(arg_c == 4 && !strcmp(arg_v[2], "utf8"))
{
test_utf8_argument();
return;
}
test_std_stream_buffering();
test_iobuf_layout();
test_std_stream_open();
test_fopen();
test_utf8(arg_v[0]);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,903 @@
/*
* Conformance tests for *printf functions.
*
* Copyright 2002 Uwe Bonnes
* Copyright 2004 Aneurin Price
* Copyright 2005 Mike McCormack
* Copyright 2015 Martin Storsjo
*
* 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
*/
#include <stdio.h>
#include <errno.h>
#include <math.h>
#include <inttypes.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine/test.h"
#define DEFINE_EXPECT(func) \
static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
#define SET_EXPECT(func) \
expect_ ## func = TRUE
#define CHECK_EXPECT2(func) \
do { \
ok(expect_ ##func, "unexpected call " #func "\n"); \
called_ ## func = TRUE; \
}while(0)
#define CHECK_EXPECT(func) \
do { \
CHECK_EXPECT2(func); \
expect_ ## func = FALSE; \
}while(0)
#define CHECK_CALLED(func) \
do { \
ok(called_ ## func, "expected " #func "\n"); \
expect_ ## func = called_ ## func = FALSE; \
}while(0)
DEFINE_EXPECT(invalid_parameter_handler);
static inline float __port_ind(void)
{
static const unsigned __ind_bytes = 0xffc00000;
return *(const float *)&__ind_bytes;
}
#define IND __port_ind()
static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
const wchar_t *function, const wchar_t *file,
unsigned line, uintptr_t arg)
{
CHECK_EXPECT(invalid_parameter_handler);
ok(expression == NULL, "expression is not NULL\n");
ok(function == NULL, "function is not NULL\n");
ok(file == NULL, "file is not NULL\n");
ok(line == 0, "line = %u\n", line);
ok(arg == 0, "arg = %Ix\n", arg);
}
static int WINAPIV vsprintf_wrapper(unsigned __int64 options, char *str,
size_t len, const char *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vsprintf(options, str, len, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_snprintf (void)
{
const char *tests[] = {"short", "justfit", "justfits", "muchlonger", "", "1"};
char buffer[8];
int bufsizes[] = { 0, 1, sizeof(buffer) };
unsigned int i, j;
for (j = 0; j < ARRAY_SIZE(bufsizes); j++) {
const int bufsiz = bufsizes[j];
/* Legacy _snprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const char *fmt = tests[i];
const int expect = strlen(fmt) > bufsiz ? -1 : strlen(fmt);
const int n = vsprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, bufsiz, fmt);
const int valid = n < 0 ? bufsiz : (n == bufsiz ? n : n+1);
ok (n == expect, "\"%s\": expected %d, returned %d\n",
fmt, expect, n);
ok (!memcmp (fmt, buffer, valid),
"\"%s\": rendered \"%.*s\"\n", fmt, valid, buffer);
}
/* C99 snprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const char *fmt = tests[i];
const int expect = strlen(fmt);
const int n = vsprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, bufsiz, fmt);
const int valid = n >= bufsiz ? (bufsiz > 0 ? bufsiz - 1 : 0) : n < 0 ? 0 : n;
ok (n == expect, "\"%s\": expected %d, returned %d\n",
fmt, expect, n);
ok (!memcmp (fmt, buffer, valid),
"\"%s\": rendered \"%.*s\" bufsiz %d\n", fmt, valid, buffer, bufsiz);
ok (bufsiz == 0 || buffer[valid] == '\0',
"\"%s\": Missing null termination (ret %d) - is %d (bufsiz %d)\n", fmt, n, buffer[valid], bufsiz);
}
/* swprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const char *fmt = tests[i];
const int expect = strlen(fmt) >= bufsiz ? bufsiz > 0 ? -2 : -1 : strlen(fmt);
const int n = vsprintf_wrapper (0, buffer, bufsiz, fmt);
const int valid = n < 0 ? bufsiz > 0 ? bufsiz - 1 : 0 : n;
ok (n == expect, "\"%s\": expected %d, returned %d\n",
fmt, expect, n);
ok (!memcmp (fmt, buffer, valid),
"\"%s\": rendered \"%.*s\" bufsiz %d\n", fmt, valid, buffer, bufsiz);
ok (bufsiz == 0 || buffer[valid] == '\0',
"\"%s\": Missing null termination (ret %d) - is %d\n", fmt, n, buffer[valid]);
}
}
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, NULL, 0, "abcd") == 4,
"Failure to snprintf to NULL\n");
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, NULL, 0, "abcd") == 4,
"Failure to snprintf to NULL\n");
ok (vsprintf_wrapper (0, NULL, 0, "abcd") == 4,
"Failure to snprintf to NULL\n");
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, 0, "abcd") == 4,
"Failure to snprintf to zero length buffer\n");
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, 0, "abcd") == -1,
"Failure to snprintf to zero length buffer\n");
ok (vsprintf_wrapper (0, buffer, 0, "abcd") == -1,
"Failure to snprintf to zero length buffer\n");
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, 0, "") == 0,
"Failure to snprintf a zero length string to a zero length buffer\n");
ok (vsprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, 0, "") == 0,
"Failure to snprintf a zero length string to a zero length buffer\n");
ok (vsprintf_wrapper (0, buffer, 0, "") == -1,
"Failure to snprintf a zero length string to a zero length buffer\n");
}
static int WINAPIV vswprintf_wrapper(unsigned __int64 options, wchar_t *str,
size_t len, const wchar_t *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vswprintf(options, str, len, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_swprintf (void)
{
const wchar_t str_short[] = {'s','h','o','r','t',0};
const wchar_t str_justfit[] = {'j','u','s','t','f','i','t',0};
const wchar_t str_justfits[] = {'j','u','s','t','f','i','t','s',0};
const wchar_t str_muchlonger[] = {'m','u','c','h','l','o','n','g','e','r',0};
const wchar_t str_empty[] = {0};
const wchar_t *tests[] = {str_short, str_justfit, str_justfits, str_muchlonger};
wchar_t buffer[8];
char narrow[8], narrow_fmt[16];
const int bufsiz = ARRAY_SIZE(buffer);
unsigned int i;
/* Legacy _snprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const wchar_t *fmt = tests[i];
const int expect = wcslen(fmt) > bufsiz ? -1 : wcslen(fmt);
const int n = vswprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, bufsiz, fmt);
const int valid = n < 0 ? bufsiz : (n == bufsiz ? n : n+1);
WideCharToMultiByte (CP_ACP, 0, buffer, -1, narrow, sizeof(narrow), NULL, NULL);
WideCharToMultiByte (CP_ACP, 0, fmt, -1, narrow_fmt, sizeof(narrow_fmt), NULL, NULL);
ok (n == expect, "\"%s\": expected %d, returned %d\n",
narrow_fmt, expect, n);
ok (!memcmp (fmt, buffer, valid * sizeof(wchar_t)),
"\"%s\": rendered \"%.*s\"\n", narrow_fmt, valid, narrow);
}
/* C99 snprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const wchar_t *fmt = tests[i];
const int expect = wcslen(fmt);
const int n = vswprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, bufsiz, fmt);
const int valid = n >= bufsiz ? bufsiz - 1 : n < 0 ? 0 : n;
WideCharToMultiByte (CP_ACP, 0, buffer, -1, narrow, sizeof(narrow), NULL, NULL);
WideCharToMultiByte (CP_ACP, 0, fmt, -1, narrow_fmt, sizeof(narrow_fmt), NULL, NULL);
ok (n == expect, "\"%s\": expected %d, returned %d\n",
narrow_fmt, expect, n);
ok (!memcmp (fmt, buffer, valid * sizeof(wchar_t)),
"\"%s\": rendered \"%.*s\"\n", narrow_fmt, valid, narrow);
ok (buffer[valid] == '\0',
"\"%s\": Missing null termination (ret %d) - is %d\n", narrow_fmt, n, buffer[valid]);
}
/* swprintf style termination */
for (i = 0; i < ARRAY_SIZE(tests); i++) {
const wchar_t *fmt = tests[i];
const int expect = wcslen(fmt) >= bufsiz ? -2 : wcslen(fmt);
const int n = vswprintf_wrapper (0, buffer, bufsiz, fmt);
const int valid = n < 0 ? bufsiz - 1 : n;
WideCharToMultiByte (CP_ACP, 0, buffer, -1, narrow, sizeof(narrow), NULL, NULL);
WideCharToMultiByte (CP_ACP, 0, fmt, -1, narrow_fmt, sizeof(narrow_fmt), NULL, NULL);
ok (n == expect, "\"%s\": expected %d, returned %d\n",
narrow_fmt, expect, n);
ok (!memcmp (fmt, buffer, valid * sizeof(wchar_t)),
"\"%s\": rendered \"%.*s\"\n", narrow_fmt, valid, narrow);
ok (buffer[valid] == '\0',
"\"%s\": Missing null termination (ret %d) - is %d\n", narrow_fmt, n, buffer[valid]);
}
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, NULL, 0, str_short) == 5,
"Failure to swprintf to NULL\n");
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, NULL, 0, str_short) == 5,
"Failure to swprintf to NULL\n");
ok (vswprintf_wrapper (0, NULL, 0, str_short) == 5,
"Failure to swprintf to NULL\n");
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, 0, str_short) == 5,
"Failure to swprintf to a zero length buffer\n");
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, 0, str_short) == -1,
"Failure to swprintf to a zero length buffer\n");
ok (vswprintf_wrapper (0, buffer, 0, str_short) == -1,
"Failure to swprintf to a zero length buffer\n");
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, buffer, 0, str_empty) == 0,
"Failure to swprintf a zero length string to a zero length buffer\n");
ok (vswprintf_wrapper (_CRT_INTERNAL_PRINTF_LEGACY_VSPRINTF_NULL_TERMINATION, buffer, 0, str_empty) == 0,
"Failure to swprintf a zero length string to a zero length buffer\n");
ok (vswprintf_wrapper (0, buffer, 0, str_empty) == -1,
"Failure to swprintf a zero length string to a zero length buffer\n");
}
static int WINAPIV vfprintf_wrapper(FILE *file,
const char *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vfprintf(0, file, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_fprintf(void)
{
static const char file_name[] = "fprintf.tst";
FILE *fp = fopen(file_name, "wb");
char buf[1024];
int ret;
ret = vfprintf_wrapper(fp, "simple test\n");
ok(ret == 12, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 12, "ftell returned %d\n", ret);
ret = vfprintf_wrapper(fp, "contains%cnull\n", '\0');
ok(ret == 14, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 26, "ftell returned %d\n", ret);
fclose(fp);
fp = fopen(file_name, "rb");
fgets(buf, sizeof(buf), fp);
ret = ftell(fp);
ok(ret == 12, "ftell returned %d\n", ret);
ok(!strcmp(buf, "simple test\n"), "buf = %s\n", buf);
fgets(buf, sizeof(buf), fp);
ret = ftell(fp);
ok(ret == 26, "ret = %d\n", ret);
ok(!memcmp(buf, "contains\0null\n", 14), "buf = %s\n", buf);
fclose(fp);
fp = fopen(file_name, "wt");
ret = vfprintf_wrapper(fp, "simple test\n");
ok(ret == 12, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 13, "ftell returned %d\n", ret);
ret = vfprintf_wrapper(fp, "contains%cnull\n", '\0');
ok(ret == 14, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 28, "ftell returned %d\n", ret);
fclose(fp);
fp = fopen(file_name, "rb");
fgets(buf, sizeof(buf), fp);
ret = ftell(fp);
ok(ret == 13, "ftell returned %d\n", ret);
ok(!strcmp(buf, "simple test\r\n"), "buf = %s\n", buf);
fgets(buf, sizeof(buf), fp);
ret = ftell(fp);
ok(ret == 28, "ret = %d\n", ret);
ok(!memcmp(buf, "contains\0null\r\n", 15), "buf = %s\n", buf);
fclose(fp);
unlink(file_name);
}
static int WINAPIV vfwprintf_wrapper(FILE *file,
const wchar_t *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vfwprintf(0, file, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_fwprintf(void)
{
static const char file_name[] = "fprintf.tst";
static const WCHAR simple[] = {'s','i','m','p','l','e',' ','t','e','s','t','\n',0};
static const WCHAR cont_fmt[] = {'c','o','n','t','a','i','n','s','%','c','n','u','l','l','\n',0};
static const WCHAR cont[] = {'c','o','n','t','a','i','n','s','\0','n','u','l','l','\n',0};
FILE *fp = fopen(file_name, "wb");
wchar_t bufw[1024];
char bufa[1024];
int ret;
ret = vfwprintf_wrapper(fp, simple);
ok(ret == 12, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 24, "ftell returned %d\n", ret);
ret = vfwprintf_wrapper(fp, cont_fmt, '\0');
ok(ret == 14, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 52, "ftell returned %d\n", ret);
fclose(fp);
fp = fopen(file_name, "rb");
fgetws(bufw, ARRAY_SIZE(bufw), fp);
ret = ftell(fp);
ok(ret == 24, "ftell returned %d\n", ret);
ok(!wcscmp(bufw, simple), "buf = %s\n", wine_dbgstr_w(bufw));
fgetws(bufw, ARRAY_SIZE(bufw), fp);
ret = ftell(fp);
ok(ret == 52, "ret = %d\n", ret);
ok(!memcmp(bufw, cont, 28), "buf = %s\n", wine_dbgstr_w(bufw));
fclose(fp);
fp = fopen(file_name, "wt");
ret = vfwprintf_wrapper(fp, simple);
ok(ret == 12, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 13, "ftell returned %d\n", ret);
ret = vfwprintf_wrapper(fp, cont_fmt, '\0');
ok(ret == 14, "ret = %d\n", ret);
ret = ftell(fp);
ok(ret == 28, "ftell returned %d\n", ret);
fclose(fp);
fp = fopen(file_name, "rb");
fgets(bufa, sizeof(bufa), fp);
ret = ftell(fp);
ok(ret == 13, "ftell returned %d\n", ret);
ok(!strcmp(bufa, "simple test\r\n"), "buf = %s\n", bufa);
fgets(bufa, sizeof(bufa), fp);
ret = ftell(fp);
ok(ret == 28, "ret = %d\n", ret);
ok(!memcmp(bufa, "contains\0null\r\n", 15), "buf = %s\n", bufa);
fclose(fp);
unlink(file_name);
/* NULL format */
errno = 0xdeadbeef;
SET_EXPECT(invalid_parameter_handler);
ret = vfwprintf_wrapper(fp, NULL);
ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno);
ok(ret == -1, "expected ret -1, got %d\n", ret);
CHECK_CALLED(invalid_parameter_handler);
/* NULL file */
errno = 0xdeadbeef;
SET_EXPECT(invalid_parameter_handler);
ret = vfwprintf_wrapper(NULL, simple);
ok(errno == EINVAL, "expected errno EINVAL, got %d\n", errno);
ok(ret == -1, "expected ret -1, got %d\n", ret);
CHECK_CALLED(invalid_parameter_handler);
/* format using % with NULL arglist*/
/* crashes on Windows */
/* ret = __stdio_common_vfwprintf(0, fp, cont_fmt, NULL, NULL); */
}
static int WINAPIV _vsnprintf_s_wrapper(char *str, size_t sizeOfBuffer,
size_t count, const char *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vsnprintf_s(0, str, sizeOfBuffer, count, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_vsnprintf_s(void)
{
const char format[] = "AB%uC";
const char out7[] = "AB123C";
const char out6[] = "AB123";
const char out2[] = "A";
const char out1[] = "";
char buffer[14] = { 0 };
int exp, got;
/* Enough room. */
exp = strlen(out7);
got = _vsnprintf_s_wrapper(buffer, 14, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
got = _vsnprintf_s_wrapper(buffer, 12, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
got = _vsnprintf_s_wrapper(buffer, 7, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out7, buffer), "buffer wrong, got=%s\n", buffer);
/* Not enough room. */
exp = -1;
got = _vsnprintf_s_wrapper(buffer, 6, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out6, buffer), "buffer wrong, got=%s\n", buffer);
got = _vsnprintf_s_wrapper(buffer, 2, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out2, buffer), "buffer wrong, got=%s\n", buffer);
got = _vsnprintf_s_wrapper(buffer, 1, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !strcmp(out1, buffer), "buffer wrong, got=%s\n", buffer);
}
static int WINAPIV _vsnwprintf_s_wrapper(WCHAR *str, size_t sizeOfBuffer,
size_t count, const WCHAR *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vsnwprintf_s(0, str, sizeOfBuffer, count, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_vsnwprintf_s(void)
{
const WCHAR format[] = {'A','B','%','u','C',0};
const WCHAR out7[] = {'A','B','1','2','3','C',0};
const WCHAR out6[] = {'A','B','1','2','3',0};
const WCHAR out2[] = {'A',0};
const WCHAR out1[] = {0};
WCHAR buffer[14] = { 0 };
int exp, got;
/* Enough room. */
exp = lstrlenW(out7);
got = _vsnwprintf_s_wrapper(buffer, 14, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out7, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
got = _vsnwprintf_s_wrapper(buffer, 12, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out7, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
got = _vsnwprintf_s_wrapper(buffer, 7, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out7, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
/* Not enough room. */
exp = -1;
got = _vsnwprintf_s_wrapper(buffer, 6, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out6, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
got = _vsnwprintf_s_wrapper(buffer, 2, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out2, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
got = _vsnwprintf_s_wrapper(buffer, 1, _TRUNCATE, format, 123);
ok( exp == got, "length wrong, expect=%d, got=%d\n", exp, got);
ok( !lstrcmpW(out1, buffer), "buffer wrong, got=%s\n", wine_dbgstr_w(buffer));
}
static void test_printf_legacy_wide(void)
{
const wchar_t wide[] = {'A','B','C','D',0};
const char narrow[] = "abcd";
const char out[] = "abcd ABCD";
/* The legacy wide flag doesn't affect narrow printfs, so the same
* format should behave the same both with and without the flag. */
const char narrow_fmt[] = "%s %ls";
/* The standard behaviour is to use the same format as for the narrow
* case, while the legacy case has got a different meaning for %s. */
const wchar_t std_wide_fmt[] = {'%','s',' ','%','l','s',0};
const wchar_t legacy_wide_fmt[] = {'%','h','s',' ','%','s',0};
char buffer[20];
wchar_t wbuffer[20];
vsprintf_wrapper(0, buffer, sizeof(buffer), narrow_fmt, narrow, wide);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS, buffer, sizeof(buffer), narrow_fmt, narrow, wide);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vswprintf_wrapper(0, wbuffer, sizeof(wbuffer), std_wide_fmt, narrow, wide);
WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), NULL, NULL);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
vswprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS, wbuffer, sizeof(wbuffer), legacy_wide_fmt, narrow, wide);
WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), NULL, NULL);
ok(!strcmp(buffer, out), "buffer wrong, got=%s\n", buffer);
}
static void test_printf_legacy_msvcrt(void)
{
char buf[50];
/* In standard mode, %F is a float format conversion, while it is a
* length modifier in legacy msvcrt mode. In legacy mode, N is also
* a length modifier. */
vsprintf_wrapper(0, buf, sizeof(buf), "%F", 1.23);
ok(!strcmp(buf, "1.230000"), "buf = %s\n", buf);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%Fd %Nd", 123, 456);
ok(!strcmp(buf, "123 456"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F %f %e %E %g %G", INFINITY, INFINITY, -INFINITY, INFINITY, INFINITY, INFINITY, INFINITY);
ok(!strcmp(buf, "inf INF -inf inf INF inf INF"), "buf = %s\n", buf);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", INFINITY);
ok(!strcmp(buf, "1.#INF00"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F", NAN, NAN);
ok(!strcmp(buf, "nan NAN"), "buf = %s\n", buf);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", NAN);
ok(!strcmp(buf, "1.#QNAN0"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%f %F", IND, IND);
ok(!strcmp(buf, "-nan(ind) -NAN(IND)"), "buf = %s\n", buf);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY, buf, sizeof(buf), "%f", IND);
ok(!strcmp(buf, "-1.#IND00"), "buf = %s\n", buf);
}
static void test_printf_legacy_three_digit_exp(void)
{
char buf[20];
vsprintf_wrapper(0, buf, sizeof(buf), "%E", 1.23);
ok(!strcmp(buf, "1.230000E+00"), "buf = %s\n", buf);
vsprintf_wrapper(_CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS, buf, sizeof(buf), "%E", 1.23);
ok(!strcmp(buf, "1.230000E+000"), "buf = %s\n", buf);
vsprintf_wrapper(0, buf, sizeof(buf), "%E", 1.23e+123);
ok(!strcmp(buf, "1.230000E+123"), "buf = %s\n", buf);
}
static void test_printf_c99(void)
{
char buf[30];
int i;
/* The msvcrt compatibility flag doesn't affect whether 'z' is interpreted
* as size_t size for integers. */
for (i = 0; i < 2; i++) {
unsigned __int64 options = (i == 0) ? 0 :
_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY;
/* z modifier accepts size_t argument */
vsprintf_wrapper(options, buf, sizeof(buf), "%zx %d", SIZE_MAX, 1);
if (sizeof(size_t) == 8)
ok(!strcmp(buf, "ffffffffffffffff 1"), "buf = %s\n", buf);
else
ok(!strcmp(buf, "ffffffff 1"), "buf = %s\n", buf);
/* j modifier with signed format accepts intmax_t argument */
vsprintf_wrapper(options, buf, sizeof(buf), "%jd %d", INTMAX_MIN, 1);
ok(!strcmp(buf, "-9223372036854775808 1"), "buf = %s\n", buf);
/* j modifier with unsigned format accepts uintmax_t argument */
vsprintf_wrapper(options, buf, sizeof(buf), "%ju %d", UINTMAX_MAX, 1);
ok(!strcmp(buf, "18446744073709551615 1"), "buf = %s\n", buf);
/* t modifier accepts ptrdiff_t argument */
vsprintf_wrapper(options, buf, sizeof(buf), "%td %d", PTRDIFF_MIN, 1);
if (sizeof(ptrdiff_t) == 8)
ok(!strcmp(buf, "-9223372036854775808 1"), "buf = %s\n", buf);
else
ok(!strcmp(buf, "-2147483648 1"), "buf = %s\n", buf);
}
}
static void test_printf_natural_string(void)
{
const wchar_t wide[] = {'A','B','C','D',0};
const char narrow[] = "abcd";
const char narrow_fmt[] = "%s %Ts";
const char narrow_out[] = "abcd abcd";
const wchar_t wide_fmt[] = {'%','s',' ','%','T','s',0};
const wchar_t wide_out[] = {'a','b','c','d',' ','A','B','C','D',0};
char buffer[20];
wchar_t wbuffer[20];
vsprintf_wrapper(0, buffer, sizeof(buffer), narrow_fmt, narrow, narrow);
ok(!strcmp(buffer, narrow_out), "buffer wrong, got=%s\n", buffer);
vswprintf_wrapper(0, wbuffer, sizeof(wbuffer), wide_fmt, narrow, wide);
ok(!lstrcmpW(wbuffer, wide_out), "buffer wrong, got=%s\n", wine_dbgstr_w(wbuffer));
}
static void test_printf_fp(void)
{
static const int flags[] = {
0,
_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY,
_CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS,
_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY
| _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS,
_CRT_INTERNAL_PRINTF_LEGACY_MSVCRT_COMPATIBILITY
| _CRT_INTERNAL_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS
| _CRT_INTERNAL_PRINTF_STANDARD_ROUNDING
};
const struct {
const char *fmt;
double d;
const char *res[ARRAY_SIZE(flags)];
const char *broken[ARRAY_SIZE(flags)];
} tests[] = {
{ "%a", NAN, { "nan", "0x1.#QNAN00000000p+0", "nan", "0x1.#QNAN00000000p+0" }},
{ "%A", NAN, { "NAN", "0X1.#QNAN00000000P+0", "NAN", "0X1.#QNAN00000000P+0" }},
{ "%e", NAN, { "nan", "1.#QNAN0e+00", "nan", "1.#QNAN0e+000" }},
{ "%E", NAN, { "NAN", "1.#QNAN0E+00", "NAN", "1.#QNAN0E+000" }},
{ "%g", NAN, { "nan", "1.#QNAN", "nan", "1.#QNAN" }},
{ "%G", NAN, { "NAN", "1.#QNAN", "NAN", "1.#QNAN" }},
{ "%21a", NAN, { " nan", " 0x1.#QNAN00000000p+0", " nan", " 0x1.#QNAN00000000p+0" }},
{ "%20e", NAN, { " nan", " 1.#QNAN0e+00", " nan", " 1.#QNAN0e+000" }},
{ "%20g", NAN, { " nan", " 1.#QNAN", " nan", " 1.#QNAN" }},
{ "%.21a", NAN, { "nan", "0x1.#QNAN0000000000000000p+0", "nan", "0x1.#QNAN0000000000000000p+0" }},
{ "%.20e", NAN, { "nan", "1.#QNAN000000000000000e+00", "nan", "1.#QNAN000000000000000e+000" }},
{ "%.20g", NAN, { "nan", "1.#QNAN", "nan", "1.#QNAN" }},
{ "%.021a", NAN, { "nan", "0x1.#QNAN0000000000000000p+0", "nan", "0x1.#QNAN0000000000000000p+0" }},
{ "%.020e", NAN, { "nan", "1.#QNAN000000000000000e+00", "nan", "1.#QNAN000000000000000e+000" }},
{ "%.020g", NAN, { "nan", "1.#QNAN", "nan", "1.#QNAN" }},
{ "%#.21a", NAN, { "nan", "0x1.#QNAN0000000000000000p+0", "nan", "0x1.#QNAN0000000000000000p+0" }},
{ "%#.20e", NAN, { "nan", "1.#QNAN000000000000000e+00", "nan", "1.#QNAN000000000000000e+000" }},
{ "%#.20g", NAN, { "nan", "1.#QNAN00000000000000", "nan", "1.#QNAN00000000000000" }},
{ "%.1g", NAN, { "nan", "1", "nan", "1" }},
{ "%.2g", NAN, { "nan", "1.$", "nan", "1.$" }},
{ "%.3g", NAN, { "nan", "1.#R", "nan", "1.#R" }},
{ "%a", IND, { "-nan(ind)", "-0x1.#IND000000000p+0", "-nan(ind)", "-0x1.#IND000000000p+0" }},
{ "%e", IND, { "-nan(ind)", "-1.#IND00e+00", "-nan(ind)", "-1.#IND00e+000" }},
{ "%g", IND, { "-nan(ind)", "-1.#IND", "-nan(ind)", "-1.#IND" }},
{ "%21a", IND, { " -nan(ind)", "-0x1.#IND000000000p+0", " -nan(ind)", "-0x1.#IND000000000p+0" }},
{ "%20e", IND, { " -nan(ind)", " -1.#IND00e+00", " -nan(ind)", " -1.#IND00e+000" }},
{ "%20g", IND, { " -nan(ind)", " -1.#IND", " -nan(ind)", " -1.#IND" }},
{ "%.21a", IND, { "-nan(ind)", "-0x1.#IND00000000000000000p+0", "-nan(ind)", "-0x1.#IND00000000000000000p+0" }},
{ "%.20e", IND, { "-nan(ind)", "-1.#IND0000000000000000e+00", "-nan(ind)", "-1.#IND0000000000000000e+000" }},
{ "%.20g", IND, { "-nan(ind)", "-1.#IND", "-nan(ind)", "-1.#IND" }},
{ "%.021a", IND, { "-nan(ind)", "-0x1.#IND00000000000000000p+0", "-nan(ind)", "-0x1.#IND00000000000000000p+0" }},
{ "%.020e", IND, { "-nan(ind)", "-1.#IND0000000000000000e+00", "-nan(ind)", "-1.#IND0000000000000000e+000" }},
{ "%.020g", IND, { "-nan(ind)", "-1.#IND", "-nan(ind)", "-1.#IND" }},
{ "%#.21a", IND, { "-nan(ind)", "-0x1.#IND00000000000000000p+0", "-nan(ind)", "-0x1.#IND00000000000000000p+0" }},
{ "%#.20e", IND, { "-nan(ind)", "-1.#IND0000000000000000e+00", "-nan(ind)", "-1.#IND0000000000000000e+000" }},
{ "%#.20g", IND, { "-nan(ind)", "-1.#IND000000000000000", "-nan(ind)", "-1.#IND000000000000000" }},
{ "%a", INFINITY, { "inf", "0x1.#INF000000000p+0", "inf", "0x1.#INF000000000p+0" }},
{ "%e", INFINITY, { "inf", "1.#INF00e+00", "inf", "1.#INF00e+000" }},
{ "%g", INFINITY, { "inf", "1.#INF", "inf", "1.#INF" }},
{ "%21a", INFINITY, { " inf", " 0x1.#INF000000000p+0", " inf", " 0x1.#INF000000000p+0" }},
{ "%20e", INFINITY, { " inf", " 1.#INF00e+00", " inf", " 1.#INF00e+000" }},
{ "%20g", INFINITY, { " inf", " 1.#INF", " inf", " 1.#INF" }},
{ "%.21a", INFINITY, { "inf", "0x1.#INF00000000000000000p+0", "inf", "0x1.#INF00000000000000000p+0" }},
{ "%.20e", INFINITY, { "inf", "1.#INF0000000000000000e+00", "inf", "1.#INF0000000000000000e+000" }},
{ "%.20g", INFINITY, { "inf", "1.#INF", "inf", "1.#INF" }},
{ "%.021a", INFINITY, { "inf", "0x1.#INF00000000000000000p+0", "inf", "0x1.#INF00000000000000000p+0" }},
{ "%.020e", INFINITY, { "inf", "1.#INF0000000000000000e+00", "inf", "1.#INF0000000000000000e+000" }},
{ "%.020g", INFINITY, { "inf", "1.#INF", "inf", "1.#INF" }},
{ "%#.21a", INFINITY, { "inf", "0x1.#INF00000000000000000p+0", "inf", "0x1.#INF00000000000000000p+0" }},
{ "%#.20e", INFINITY, { "inf", "1.#INF0000000000000000e+00", "inf", "1.#INF0000000000000000e+000" }},
{ "%#.20g", INFINITY, { "inf", "1.#INF000000000000000", "inf", "1.#INF000000000000000" }},
{ "%a", -INFINITY, { "-inf", "-0x1.#INF000000000p+0", "-inf", "-0x1.#INF000000000p+0" }},
{ "%e", -INFINITY, { "-inf", "-1.#INF00e+00", "-inf", "-1.#INF00e+000" }},
{ "%g", -INFINITY, { "-inf", "-1.#INF", "-inf", "-1.#INF" }},
{ "%21a", -INFINITY, { " -inf", "-0x1.#INF000000000p+0", " -inf", "-0x1.#INF000000000p+0" }},
{ "%20e", -INFINITY, { " -inf", " -1.#INF00e+00", " -inf", " -1.#INF00e+000" }},
{ "%20g", -INFINITY, { " -inf", " -1.#INF", " -inf", " -1.#INF" }},
{ "%.21a", -INFINITY, { "-inf", "-0x1.#INF00000000000000000p+0", "-inf", "-0x1.#INF00000000000000000p+0" }},
{ "%.20e", -INFINITY, { "-inf", "-1.#INF0000000000000000e+00", "-inf", "-1.#INF0000000000000000e+000" }},
{ "%.20g", -INFINITY, { "-inf", "-1.#INF", "-inf", "-1.#INF" }},
{ "%.021a", -INFINITY, { "-inf", "-0x1.#INF00000000000000000p+0", "-inf", "-0x1.#INF00000000000000000p+0" }},
{ "%.020e", -INFINITY, { "-inf", "-1.#INF0000000000000000e+00", "-inf", "-1.#INF0000000000000000e+000" }},
{ "%.020g", -INFINITY, { "-inf", "-1.#INF", "-inf", "-1.#INF" }},
{ "%#.21a", -INFINITY, { "-inf", "-0x1.#INF00000000000000000p+0", "-inf", "-0x1.#INF00000000000000000p+0" }},
{ "%#.20e", -INFINITY, { "-inf", "-1.#INF0000000000000000e+00", "-inf", "-1.#INF0000000000000000e+000" }},
{ "%#.20g", -INFINITY, { "-inf", "-1.#INF000000000000000", "-inf", "-1.#INF000000000000000" }},
{ "%a", 0, { "0x0.0000000000000p+0" }},
{ "%A", 0, { "0X0.0000000000000P+0" }},
{ "%a", 0.5, { "0x1.0000000000000p-1" }},
{ "%a", 1, { "0x1.0000000000000p+0" }},
{ "%a", 20, { "0x1.4000000000000p+4" }},
{ "%a", -1, { "-0x1.0000000000000p+0" }},
{ "%a", 0.1, { "0x1.999999999999ap-4" }},
{ "%24a", 0.1, { " 0x1.999999999999ap-4" }},
{ "%024a", 0.1, { "0x00001.999999999999ap-4" }},
{ "%.2a", 0.1, { "0x1.9ap-4" }},
{ "%.20a", 0.1, { "0x1.999999999999a0000000p-4" }},
{ "%.a", 0.1e-20, { "0x1p-70" }},
{ "%a", 0.1e-20, { "0x1.2e3b40a0e9b4fp-70" }},
{ "%a", 4.9406564584124654e-324, { "0x0.0000000000001p-1022" }},
{ "%.0a", -1.5, { "-0x2p+0" }, { "-0x1p+0" }},
{ "%.0a", -0.5, { "-0x1p-1" }},
{ "%.0a", 0.5, { "0x1p-1" }},
{ "%.0a", 1.5, { "0x2p+0" }, { "0x1p+0" }},
{ "%.0a", 1.99, { "0x2p+0" }},
{ "%.0a", 2, { "0x1p+1" }},
{ "%.0a", 9.5, { "0x1p+3" }},
{ "%.0a", 10.5, { "0x1p+3" }},
{ "%#.0a", -1.5, { "-0x2.p+0" }, { "-0x1.p+0" }},
{ "%#.0a", -0.5, { "-0x1.p-1" }},
{ "%#.0a", 0.5, { "0x1.p-1" }},
{ "%#.0a", 1.5, { "0x2.p+0" }, { "0x1.p+0" }},
{ "%#.1a", 1.03125, { "0x1.1p+0", NULL, NULL, NULL, "0x1.0p+0" }, { "0x1.0p+0" }},
{ "%#.1a", 1.09375, { "0x1.2p+0" }, { "0x1.1p+0" }},
{ "%#.1a", 1.15625, { "0x1.3p+0", NULL, NULL, NULL, "0x1.2p+0" }, { "0x1.2p+0" }},
{ "%f", 0, { "0.000000" }},
{ "%e", 0, { "0.000000e+00", NULL, "0.000000e+000" }},
{ "%g", 0, { "0" }},
{ "%21f", 0, { " 0.000000" }},
{ "%20e", 0, { " 0.000000e+00", NULL, " 0.000000e+000" }},
{ "%20g", 0, { " 0" }},
{ "%.21f", 0, { "0.000000000000000000000" }},
{ "%.20e", 0, { "0.00000000000000000000e+00", NULL, "0.00000000000000000000e+000" }},
{ "%.20g", 0, { "0" }},
{ "%.021f", 0, { "0.000000000000000000000" }},
{ "%.020e", 0, { "0.00000000000000000000e+00", NULL, "0.00000000000000000000e+000" }},
{ "%.020g", 0, { "0" }},
{ "%#.21f", 0, { "0.000000000000000000000" }},
{ "%#.20e", 0, { "0.00000000000000000000e+00", NULL, "0.00000000000000000000e+000" }},
{ "%#.20g", 0, { "0.0000000000000000000" }, { "0.00000000000000000000" }},
{ "%f", 123, { "123.000000" }},
{ "%e", 123, { "1.230000e+02", NULL, "1.230000e+002" }},
{ "%g", 123, { "123" }},
{ "%21f", 123, { " 123.000000" }},
{ "%20e", 123, { " 1.230000e+02", NULL, " 1.230000e+002" }},
{ "%20g", 123, { " 123" }},
{ "%.21f", 123, { "123.000000000000000000000" }},
{ "%.20e", 123, { "1.23000000000000000000e+02", NULL, "1.23000000000000000000e+002" }},
{ "%.20g", 123, { "123" }},
{ "%.021f", 123, { "123.000000000000000000000" }},
{ "%.020e", 123, { "1.23000000000000000000e+02", NULL, "1.23000000000000000000e+002" }},
{ "%.020g", 123, { "123" }},
{ "%#.21f", 123, { "123.000000000000000000000" }},
{ "%#.20e", 123, { "1.23000000000000000000e+02", NULL, "1.23000000000000000000e+002" }},
{ "%#.20g", 123, { "123.00000000000000000" }},
{ "%f", -765, { "-765.000000" }},
{ "%e", -765, { "-7.650000e+02", NULL, "-7.650000e+002" }},
{ "%g", -765, { "-765" }},
{ "%21f", -765, { " -765.000000" }},
{ "%20e", -765, { " -7.650000e+02", NULL, " -7.650000e+002" }},
{ "%20g", -765, { " -765" }},
{ "%.21f", -765, { "-765.000000000000000000000" }},
{ "%.20e", -765, { "-7.65000000000000000000e+02", NULL, "-7.65000000000000000000e+002" }},
{ "%.20g", -765, { "-765" }},
{ "%.021f", -765, { "-765.000000000000000000000" }},
{ "%.020e", -765, { "-7.65000000000000000000e+02", NULL, "-7.65000000000000000000e+002" }},
{ "%.020g", -765, { "-765" }},
{ "%#.21f", -765, { "-765.000000000000000000000" }},
{ "%#.20e", -765, { "-7.65000000000000000000e+02", NULL, "-7.65000000000000000000e+002" }},
{ "%#.20g", -765, { "-765.00000000000000000" }},
{ "%.30f", 1.0/3.0, { "0.333333333333333314829616256247" }},
{ "%.30lf", sqrt(2), { "1.414213562373095145474621858739" }},
{ "%f", 3.141592653590000, { "3.141593" }},
{ "%.10f", 3.141592653590000, { "3.1415926536" }},
{ "%.11f", 3.141592653590000, { "3.14159265359" }},
{ "%.15f", 3.141592653590000, { "3.141592653590000" }},
{ "%.15f", M_PI, { "3.141592653589793" }},
{ "%.13f", 37.866261574537077, { "37.8662615745371" }},
{ "%.14f", 37.866261574537077, { "37.86626157453708" }},
{ "%.15f", 37.866261574537077, { "37.866261574537077" }},
{ "%.0g", 9.8949714229143402e-05, { "0.0001" }},
{ "%.0f", 0.5, { "1", NULL, NULL, NULL, "0" }, {NULL, NULL, NULL, NULL, "1" }},
{ "%.0f", 1.5, { "2" }},
{ "%.0f", 2.5, { "3", NULL, NULL, NULL, "2" }, {NULL, NULL, NULL, NULL, "3" }},
{ "%g", 9.999999999999999e-5, { "0.0001" }},
{ "%g", 0.0005, { "0.0005" }},
{ "%g", 0.00005, { "5e-05", NULL, "5e-005" }},
{ "%g", 0.000005, { "5e-06", NULL, "5e-006" }},
{ "%g", 999999999999999.0, { "1e+15", NULL, "1e+015" }},
{ "%g", 1000000000000000.0, { "1e+15", NULL, "1e+015" }},
{ "%.15g", 0.0005, { "0.0005" }},
{ "%.15g", 0.00005, { "5e-05", NULL, "5e-005" }},
{ "%.15g", 0.000005, { "5e-06", NULL, "5e-006" }},
{ "%.15g", 999999999999999.0, { "999999999999999" }},
{ "%.15g", 1000000000000000.0, { "1e+15", NULL, "1e+015" }},
};
const char *res = NULL;
const char *broken_res;
char buf[100];
int i, j, r;
for (i = 0; i < ARRAY_SIZE(tests); i++)
{
broken_res = NULL;
for (j = 0; j < ARRAY_SIZE(flags); j++)
{
if (tests[i].res[j]) res = tests[i].res[j];
if (tests[i].broken[j]) broken_res = tests[i].broken[j];
r = vsprintf_wrapper(flags[j], buf, sizeof(buf), tests[i].fmt, tests[i].d);
ok(r == strlen(res) || broken(broken_res && r == strlen(broken_res)),
"%d,%d) r = %d, expected %Id\n", i, j, r, strlen(res));
ok(!strcmp(buf, res) || broken(broken_res && !strcmp(buf, broken_res)),
"%d,%d) buf = %s, expected %s\n", i, j, buf, res);
}
}
}
static void test_printf_width_specification(void)
{
int r;
char buffer[20];
r = vsprintf_wrapper(0, buffer, sizeof(buffer), "%0*2d", 1, 3);
ok(r == 2, "r = %d\n", r);
ok(!strcmp(buffer, "03"), "buffer wrong, got=%s\n", buffer);
r = vsprintf_wrapper(0, buffer, sizeof(buffer), "%*0d", 1, 2);
ok(r == 1, "r = %d\n", r);
ok(!strcmp(buffer, "2"), "buffer wrong, got=%s\n", buffer);
r = vsprintf_wrapper(0, buffer, sizeof(buffer), "% *2d", 0, 7);
ok(r == 2, "r = %d\n", r);
ok(!strcmp(buffer, " 7"), "buffer wrong, got=%s\n", buffer);
}
START_TEST(printf)
{
ok(_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL,
"Invalid parameter handler was already set\n");
test_snprintf();
test_swprintf();
test_fprintf();
test_fwprintf();
test_vsnprintf_s();
test_vsnwprintf_s();
test_printf_legacy_wide();
test_printf_legacy_msvcrt();
test_printf_legacy_three_digit_exp();
test_printf_c99();
test_printf_natural_string();
test_printf_fp();
test_printf_width_specification();
}

View file

@ -0,0 +1,330 @@
/*
* Conformance tests for *scanf functions.
*
* Copyright 2002 Uwe Bonnes
*
* 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
*/
#include <stdio.h>
#include <math.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine/test.h"
static int WINAPIV vsscanf_wrapper(unsigned __int64 options, const char *str, size_t len, const char *format, ...)
{
int ret;
va_list valist;
va_start(valist, format);
ret = __stdio_common_vsscanf(options, str, len, format, NULL, valist);
va_end(valist);
return ret;
}
static void test_sscanf(void)
{
static const float float1 = -82.6267f, float2 = 27.76f;
char buffer[100], buffer1[100];
int result, ret, hour, min, count;
LONGLONG result64;
DWORD_PTR result_ptr;
char c;
void *ptr;
float ret_float1, ret_float2;
double double_res;
unsigned int i;
size_t ret_size;
static const unsigned int tests[] =
{
0,
_CRT_INTERNAL_SCANF_LEGACY_WIDE_SPECIFIERS,
_CRT_INTERNAL_SCANF_LEGACY_MSVCRT_COMPATIBILITY,
};
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
ret = vsscanf_wrapper(tests[i], "", -1, "%d", &result);
ok(ret == EOF, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "000000000046F170", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)0x46f170, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "0046F171", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)0x46f171, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "46F172", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)0x46f172, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "0x46F173", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
todo_wine ok(ptr == (void *)0x46f173, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "-46F174", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)(ULONG_PTR)-0x46f174, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "+46F175", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)0x46f175, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "1233", -1, "%p", &ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ptr == (void *)0x1233, "sscanf reads %p for flags %#x\n", ptr, tests[i]);
ret = vsscanf_wrapper(tests[i], "1234", -1, "%P", &ptr);
todo_wine ok(ret == 0, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "0x519", -1, "%x", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 0x519, "sscanf reads %#x for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "0x51a", -1, "%x", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 0x51a, "sscanf reads %#x for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "0x51g", -1, "%x", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 0x51, "sscanf reads %#x for flags %#x\n", result, tests[i]);
result = 0;
ret = vsscanf_wrapper(tests[i], "-1", -1, "%x", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == -1, "sscanf reads %#x for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "\"%12@", -1, "%\"%%%d%@", &result);
todo_wine ok(ret == 0, "sscanf returned %d for flags %#x\n", ret, tests[i]);
sprintf(buffer, "%f %f", float1, float2);
ret = vsscanf_wrapper(tests[i], buffer, -1, "%f%f", &ret_float1, &ret_float2);
ok(ret == 2, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ret_float1 == float1, "got wrong float %.8e for flags %#x\n", ret_float1, tests[i]);
ok(ret_float2 == float2, "got wrong float %.8e for flags %#x\n", ret_float2, tests[i]);
sprintf(buffer, "%lf", 32.715);
ret = vsscanf_wrapper(tests[i], buffer, -1, "%lf", &double_res);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 32.715, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ret = vsscanf_wrapper(tests[i], buffer, -1, "%Lf", &double_res);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 32.715, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ret = vsscanf_wrapper(tests[i], "1.1e-30", -1, "%lf", &double_res);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 1.1e-30, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ret = vsscanf_wrapper(tests[i], " Waverly", -1, "%*c%[^\n]", buffer);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(!strcmp(buffer, " Waverly"), "got string '%s' for flags %#x\n", buffer, tests[i]);
ret = vsscanf_wrapper(tests[i], "abcefgdh", -1, "%*[a-cg-e]%c", &buffer[0]);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(buffer[0] == 'd', "got char '%c' for flags %#x\n", buffer[0], tests[i]);
ret = vsscanf_wrapper(tests[i], "abcefgdh", -1, "%*[a-cd-dg-e]%c", &buffer[0]);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(buffer[0] == 'h', "got char '%c' for flags %#x\n", buffer[0], tests[i]);
strcpy(buffer, "foo");
strcpy(buffer1, "bar");
ret = vsscanf_wrapper(tests[i], "a", -1, "%s%s", buffer, buffer1);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(!strcmp(buffer, "a"), "got string '%s' for flags %#x\n", buffer, tests[i]);
ok(!strcmp(buffer1, "bar"), "got string '%s' for flags %#x\n", buffer1, tests[i]);
ret = vsscanf_wrapper(tests[i], "21:59:20", -1, "%d%n", &result, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 21, "got wrong number %d for flags %#x\n", result, tests[i]);
ok(count == 2, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], ":59:20", -1, "%*c%n", &count);
ok(ret == 0, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(count == 1, "got wrong count %d for flags %#x\n", count, tests[i]);
result = 0xdeadbeef;
ret = vsscanf_wrapper(tests[i], "12345678", -1, "%hd", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 0xdead614e, "got wrong number %#x for flags %#x\n", result, tests[i]);
result = 0xdeadbeef;
ret = vsscanf_wrapper(tests[i], "12345678", -1, "%hhd", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 0xdeadbe4e, "got wrong number %#x for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "12345678901234", -1, "%lld", &result64);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result64 == 12345678901234, "got wrong number 0x%s for flags %#x\n",
wine_dbgstr_longlong(result64), tests[i]);
ret = vsscanf_wrapper(tests[i], "123", -1, "%i", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 123, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "-1", -1, "%i", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == -1, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "123", -1, "%d", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 123, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "-1", -1, "%d", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == -1, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "017", -1, "%i", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 15, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "0x17", -1, "%i", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == 23, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = vsscanf_wrapper(tests[i], "-1", -1, "%o", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == -1, "got wrong number %d for flags %#x\n", result, tests[i]);
ret = 0xdeadbeef;
ret = vsscanf_wrapper(tests[i], "-1", -1, "%u", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == -1, "got wrong number %d for flags %#x\n", result, tests[i]);
c = 0x55;
ret = vsscanf_wrapper(tests[i], "a", -1, "%c", &c);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(c == 'a', "got wrong char '%c' for flags %#x\n", c, tests[i]);
c = 0x55;
ret = vsscanf_wrapper(tests[i], " a", -1, "%c", &c);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(c == ' ', "got wrong char '%c' for flags %#x\n", c, tests[i]);
c = 0x55;
ret = vsscanf_wrapper(tests[i], "18:59", -1, "%d:%d%c", &hour, &min, &c);
ok(ret == 2, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(hour == 18, "got wrong char '%c' for flags %#x\n", hour, tests[i]);
ok(min == 59, "got wrong char '%c' for flags %#x\n", min, tests[i]);
ok(c == 0x55, "got wrong char '%c' for flags %#x\n", c, tests[i]);
strcpy(buffer, "foo");
strcpy(buffer1, "bar");
ret = vsscanf_wrapper(tests[i], "abc def", -1, "%s %n%s", buffer, &count, buffer1);
ok(ret == 2, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(!strcmp(buffer, "abc"), "got wrong string '%s' for flags %#x\n", buffer, tests[i]);
ok(count == 6, "got wrong count %d for flags %#x\n", count, tests[i]);
ok(!strcmp(buffer1, "def"), "got wrong string '%s' for flags %#x\n", buffer1, tests[i]);
ret = vsscanf_wrapper(tests[i], "3:45", -1, "%d:%d%n", &hour, &min, &count);
ok(ret == 2, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(hour == 3, "got wrong char '%c' for flags %#x\n", hour, tests[i]);
ok(min == 45, "got wrong char '%c' for flags %#x\n", min, tests[i]);
ok(count == 4, "got wrong count %d for flags %#x\n", count, tests[i]);
strcpy(buffer, "foo");
strcpy(buffer1, "bar");
ret = vsscanf_wrapper(tests[i], "test=value\xda", -1, "%[^=] = %[^;]", buffer, buffer1);
ok(ret == 2, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(!strcmp(buffer, "test"), "got wrong string '%s' for flags %#x\n", buffer, tests[i]);
ok(!strcmp(buffer1, "value\xda"), "got wrong string '%s' for flags %#x\n", buffer1, tests[i]);
ret = vsscanf_wrapper(tests[i], "0.1", 3, "%lf%n", &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 0.1, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ok(count == 3, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], "a", -1, "%lf%n", &double_res, &count);
ok(ret == 0, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "aa", -1, "%c%lf%n", &c, &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "a0e", -1, "%c%lf%n", &c, &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "0.", -1, "%lf%n", &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 0, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ok(count == 2, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], "0.", 2, "%lf%n", &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == 0, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ok(count == 2, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], "1e", -1, "%lf%n", &double_res, &count);
ok(ret == -1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "1e ", 2, "%lf%n", &double_res, &count);
ok(ret == -1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "1e+", -1, "%lf%n", &double_res, &count);
ok(ret == -1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret = vsscanf_wrapper(tests[i], "inf", -1, "%lf%n", &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == INFINITY, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ok(count == 3, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], "infa", -1, "%lf%n", &double_res, &count);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(double_res == INFINITY, "got wrong double %.16e for flags %#x\n", double_res, tests[i]);
ok(count == 3, "got wrong count %d for flags %#x\n", count, tests[i]);
ret = vsscanf_wrapper(tests[i], "infi", -1, "%lf%n", &double_res, &count);
ok(ret == -1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ret_size = ~0;
ret = vsscanf_wrapper(tests[i], "1", -1, "%zd", &ret_size);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(ret_size == 1, "got wrong size_t %s for flags %#x\n",
wine_dbgstr_longlong((LONGLONG)ret_size), tests[i]);
result64 = 0;
ret = vsscanf_wrapper(tests[i], "12345678901234", -1, "%I64d", &result64);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result64 == 12345678901234ll, "got wrong number 0x%s for flags %#x\n",
wine_dbgstr_longlong(result64), tests[i]);
result = 0;
ret = vsscanf_wrapper(tests[i], "12345678901234", -1, "%I32d", &result);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result == (int)12345678901234ll, /* this is always truncated to 32bit */
"got wrong number 0x%d for flags %#x\n", result, tests[i]);
result_ptr = 0;
ret = vsscanf_wrapper(tests[i], "0x87654321", -1, "%Ix", &result_ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result_ptr == 0x87654321,
"got wrong number %Ix for flags %#x\n", result_ptr, tests[i]);
result_ptr = 0;
ret = vsscanf_wrapper(tests[i], "0x123456789", -1, "%Ix", &result_ptr);
ok(ret == 1, "sscanf returned %d for flags %#x\n", ret, tests[i]);
ok(result_ptr == (DWORD_PTR)0x123456789ull, /* this is truncated on 32bit systems */
"got wrong number %Ix for flags %#x\n", result_ptr, tests[i]);
}
}
START_TEST(scanf)
{
test_sscanf();
}

View file

@ -0,0 +1,860 @@
/*
* Copyright 2015 Martin Storsjo
*
* 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
*/
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <wchar.h>
#include <stdio.h>
#include <locale.h>
#include <mbctype.h>
#include <mbstring.h>
#include <windef.h>
#include <winbase.h>
#include "wine/test.h"
#include <math.h>
#define DEFINE_EXPECT(func) \
static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
#define SET_EXPECT(func) \
expect_ ## func = TRUE
#define CHECK_EXPECT2(func) \
do { \
ok(expect_ ##func, "unexpected call " #func "\n"); \
called_ ## func = TRUE; \
}while(0)
#define CHECK_EXPECT(func) \
do { \
CHECK_EXPECT2(func); \
expect_ ## func = FALSE; \
}while(0)
#define CHECK_CALLED(func) \
do { \
ok(called_ ## func, "expected " #func "\n"); \
expect_ ## func = called_ ## func = FALSE; \
}while(0)
DEFINE_EXPECT(invalid_parameter_handler);
static void __cdecl test_invalid_parameter_handler(const wchar_t *expression,
const wchar_t *function, const wchar_t *file,
unsigned line, uintptr_t arg)
{
CHECK_EXPECT(invalid_parameter_handler);
ok(expression == NULL, "expression is not NULL\n");
ok(function == NULL, "function is not NULL\n");
ok(file == NULL, "file is not NULL\n");
ok(line == 0, "line = %u\n", line);
ok(arg == 0, "arg = %Ix\n", arg);
}
_ACRTIMP int __cdecl _o_tolower(int);
_ACRTIMP int __cdecl _o_toupper(int);
static BOOL local_isnan(double d)
{
return d != d;
}
#define test_strtod_str_errno(string, value, length, err) _test_strtod_str(__LINE__, string, value, length, err)
#define test_strtod_str(string, value, length) _test_strtod_str(__LINE__, string, value, length, 0)
static void _test_strtod_str(int line, const char* string, double value, int length, int err)
{
char *end;
double d;
errno = 0xdeadbeef;
d = strtod(string, &end);
if(!err)
ok_(__FILE__, line)(errno == 0xdeadbeef, "errno = %d\n", errno);
else
ok_(__FILE__, line)(errno == err, "errno = %d\n", errno);
if (local_isnan(value))
ok_(__FILE__, line)(local_isnan(d), "d = %.16le (\"%s\")\n", d, string);
else
ok_(__FILE__, line)(d == value, "d = %.16le (\"%s\")\n", d, string);
ok_(__FILE__, line)(end == string + length, "incorrect end (%d, \"%s\")\n", (int)(end - string), string);
}
static void test_strtod(void)
{
test_strtod_str("infinity", INFINITY, 8);
test_strtod_str("INFINITY", INFINITY, 8);
test_strtod_str("InFiNiTy", INFINITY, 8);
test_strtod_str("INF", INFINITY, 3);
test_strtod_str("-inf", -INFINITY, 4);
test_strtod_str("inf42", INFINITY, 3);
test_strtod_str("inffoo", INFINITY, 3);
test_strtod_str("infini", INFINITY, 3);
test_strtod_str("input", 0, 0);
test_strtod_str("-input", 0, 0);
test_strtod_str_errno("1.7976931348623159e+308", INFINITY, 23, ERANGE);
test_strtod_str_errno("-1.7976931348623159e+308", -INFINITY, 24, ERANGE);
test_strtod_str("NAN", NAN, 3);
test_strtod_str("nan", NAN, 3);
test_strtod_str("NaN", NAN, 3);
test_strtod_str("0x42", 66, 4);
test_strtod_str("0X42", 66, 4);
test_strtod_str("-0x42", -66, 5);
test_strtod_str("0x1p1", 2, 5);
test_strtod_str("0x1P1", 2, 5);
test_strtod_str("0x1p+1", 2, 6);
test_strtod_str("0x2p-1", 1, 6);
test_strtod_str("0xA", 10, 3);
test_strtod_str("0xa", 10, 3);
test_strtod_str("0xABCDEF", 11259375, 8);
test_strtod_str("0Xabcdef", 11259375, 8);
test_strtod_str("0x1.1", 1.0625, 5);
test_strtod_str("0x1.1p1", 2.125, 7);
test_strtod_str("0x1.A", 1.625, 5);
test_strtod_str("0x1p1a", 2, 5);
test_strtod_str("0xp3", 0, 1);
test_strtod_str("0x.", 0, 1);
test_strtod_str("0x.8", 0.5, 4);
test_strtod_str("0x.8p", 0.5, 4);
test_strtod_str("0x0p10000000000000000000000000", 0, 30);
test_strtod_str("0x1p-1026", 1.3906711615670009e-309, 9);
test_strtod_str("0x1ffffffffffffe.80000000000000000000", 9007199254740990.0, 37);
test_strtod_str("0x1ffffffffffffe.80000000000000000001", 9007199254740991.0, 37);
test_strtod_str("0x1fffffffffffff.80000000000000000000", 9007199254740992.0, 37);
test_strtod_str("0x1fffffffffffff.80000000000000000001", 9007199254740992.0, 37);
test_strtod_str("4.0621786324484881721115322e-53", 4.0621786324484881721115322e-53, 31);
test_strtod_str("1.8905590910042396899370942", 1.8905590910042396899370942, 27);
test_strtod_str("1.7976931348623158e+308", 1.7976931348623158e+308, 23);
test_strtod_str("2.2250738585072014e-308", 2.2250738585072014e-308, 23);
test_strtod_str("4.9406564584124654e-324", 4.9406564584124654e-324, 23);
test_strtod_str("2.48e-324", 4.9406564584124654e-324, 9);
test_strtod_str_errno("2.47e-324", 0, 9, ERANGE);
}
static void test_strtof(void)
{
static const struct {
const char *str;
int len;
float ret;
int err;
} tests[] = {
{ "12.1", 4, 12.1f },
{ "-13.721", 7, -13.721f },
{ "1.e40", 5, INFINITY, ERANGE },
{ "-1.e40", 6, -INFINITY, ERANGE },
{ "0.0", 3, 0.0f },
{ "-0.0", 4, 0.0f },
{ "1.4e-45", 7, 1.4e-45f },
{ "-1.4e-45", 8, -1.4e-45f },
{ "1.e-60", 6, 0, ERANGE },
{ "-1.e-60", 7, 0, ERANGE },
};
char *end;
float f;
int i;
for (i=0; i<ARRAY_SIZE(tests); i++)
{
errno = 0xdeadbeef;
f = strtof(tests[i].str, &end);
ok(f == tests[i].ret, "%d) f = %.16e\n", i, f);
ok(end == tests[i].str + tests[i].len, "%d) len = %d\n",
i, (int)(end - tests[i].str));
ok(errno == tests[i].err || (!tests[i].err && errno == 0xdeadbeef),
"%d) errno = %d\n", i, errno);
}
}
static void test__memicmp(void)
{
static const char *s1 = "abc";
static const char *s2 = "aBd";
int ret;
ret = _memicmp(NULL, NULL, 0);
ok(!ret, "got %d\n", ret);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp(NULL, NULL, 1);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp(s1, NULL, 1);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp(NULL, s2, 1);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ret = _memicmp(s1, s2, 2);
ok(!ret, "got %d\n", ret);
ret = _memicmp(s1, s2, 3);
ok(ret == -1, "got %d\n", ret);
}
static void test__memicmp_l(void)
{
static const char *s1 = "abc";
static const char *s2 = "aBd";
int ret;
ret = _memicmp_l(NULL, NULL, 0, NULL);
ok(!ret, "got %d\n", ret);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp_l(NULL, NULL, 1, NULL);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp_l(s1, NULL, 1, NULL);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _memicmp_l(NULL, s2, 1, NULL);
ok(ret == _NLSCMPERROR, "got %d\n", ret);
ok(errno == EINVAL, "Unexpected errno = %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ret = _memicmp_l(s1, s2, 2, NULL);
ok(!ret, "got %d\n", ret);
ret = _memicmp_l(s1, s2, 3, NULL);
ok(ret == -1, "got %d\n", ret);
}
static void test___strncnt(void)
{
static const struct
{
const char *str;
size_t size;
size_t ret;
}
strncnt_tests[] =
{
{ "a", 0, 0 },
{ "a", 1, 1 },
{ "a", 10, 1 },
{ "abc", 1, 1 },
};
unsigned int i;
size_t ret;
for (i = 0; i < ARRAY_SIZE(strncnt_tests); ++i)
{
ret = __strncnt(strncnt_tests[i].str, strncnt_tests[i].size);
ok(ret == strncnt_tests[i].ret, "%u: unexpected return value %u.\n", i, (int)ret);
}
if (0) /* crashes */
{
ret = __strncnt(NULL, 0);
ret = __strncnt(NULL, 1);
}
}
static void test_C_locale(void)
{
int i, j;
wint_t ret, exp;
_locale_t locale;
static const char *locales[] = { NULL, "C" };
/* C locale only converts case for [a-zA-Z] */
setlocale(LC_ALL, "C");
for (i = 0; i <= 0xffff; i++)
{
ret = tolower(i);
if (i >= 'A' && i <= 'Z')
{
exp = i + 'a' - 'A';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
ret = _tolower(i);
exp = i + 'a' - 'A';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
ret = _o_tolower(i);
if (i >= 'A' && i <= 'Z')
{
exp = i + 'a' - 'A';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
ret = towlower(i);
if (i >= 'A' && i <= 'Z')
{
exp = i + 'a' - 'A';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
ret = toupper(i);
if (i >= 'a' && i <= 'z')
{
exp = i + 'A' - 'a';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
ret = _toupper(i);
exp = i + 'A' - 'a';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
ret = _o_toupper(i);
if (i >= 'a' && i <= 'z')
{
exp = i + 'A' - 'a';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
ret = towupper(i);
if (i >= 'a' && i <= 'z')
{
exp = i + 'A' - 'a';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == i, "expected self %x, got %x for C locale\n", i, ret);
}
for (i = 0; i < ARRAY_SIZE(locales); i++) {
locale = locales[i] ? _create_locale(LC_ALL, locales[i]) : NULL;
for (j = 0; j <= 0xffff; j++) {
ret = _towlower_l(j, locale);
if (j >= 'A' && j <= 'Z')
{
exp = j + 'a' - 'A';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == j, "expected self %x, got %x for C locale\n", j, ret);
ret = _towupper_l(j, locale);
if (j >= 'a' && j <= 'z')
{
exp = j + 'A' - 'a';
ok(ret == exp, "expected %x, got %x for C locale\n", exp, ret);
}
else
ok(ret == j, "expected self %x, got %x for C locale\n", j, ret);
}
_free_locale(locale);
}
}
static void test_mbsspn( void)
{
unsigned char str1[] = "cabernet";
unsigned char str2[] = "shiraz";
unsigned char set[] = "abc";
unsigned char empty[] = "";
unsigned char mbstr[] = " 2019\x94\x4e" "6\x8c\x8e" "29\x93\xfa";
unsigned char mbset1[] = "0123456789 \x94\x4e";
unsigned char mbset2[] = " \x94\x4e\x8c\x8e";
unsigned char mbset3[] = "\x8e";
int ret, cp = _getmbcp();
ret = _mbsspn(str1, set);
ok(ret == 3, "_mbsspn returns %d should be 3\n", ret);
ret = _mbsspn(str2, set);
ok(ret == 0, "_mbsspn returns %d should be 0\n", ret);
ret = _mbsspn(str1, empty);
ok(ret == 0, "_mbsspn returns %d should be 0\n", ret);
_setmbcp(932);
ret = _mbsspn(mbstr, mbset1);
ok(ret == 8, "_mbsspn returns %d should be 8\n", ret);
ret = _mbsspn(mbstr, mbset2);
ok(ret == 1, "_mbsspn returns %d should be 1\n", ret);
ret = _mbsspn(mbstr+8, mbset1);
ok(ret == 0, "_mbsspn returns %d should be 0\n", ret);
ret = _mbsspn(mbstr+8, mbset2);
ok(ret == 2, "_mbsspn returns %d should be 2\n", ret);
ret = _mbsspn(mbstr, mbset3);
ok(ret == 14, "_mbsspn returns %d should be 14\n", ret);
_setmbcp(cp);
}
static void test_wcstok(void)
{
static const wchar_t *input = L"two words";
wchar_t buffer[16];
wchar_t *token;
wchar_t *next;
next = NULL;
wcscpy(buffer, input);
token = wcstok(buffer, L" ", &next);
ok(!wcscmp(L"two", token), "expected \"two\", got \"%ls\"\n", token);
ok(next == token + 4, "expected %p, got %p\n", token + 4, next);
token = wcstok(NULL, L" ", &next);
ok(!wcscmp(L"words", token), "expected \"words\", got \"%ls\"\n", token);
ok(next == token + 5, "expected %p, got %p\n", token + 5, next);
token = wcstok(NULL, L" ", &next);
ok(!token, "expected NULL, got %p\n", token);
wcscpy(buffer, input);
token = wcstok(buffer, L" ", NULL);
ok(!wcscmp(L"two", token), "expected \"two\", got \"%ls\"\n", token);
token = wcstok(NULL, L" ", NULL);
ok(!wcscmp(L"words", token), "expected \"words\", got \"%ls\"\n", token);
token = wcstok(NULL, L" ", NULL);
ok(!token, "expected NULL, got %p\n", token);
next = NULL;
wcscpy(buffer, input);
token = wcstok(buffer, L"=", &next);
ok(!wcscmp(token, input), "expected \"%ls\", got \"%ls\"\n", input, token);
ok(next == buffer + wcslen(input), "expected %p, got %p\n", buffer + wcslen(input), next);
token = wcstok(NULL, L"=", &next);
ok(!token, "expected NULL, got \"%ls\"\n", token);
ok(next == buffer + wcslen(input), "expected %p, got %p\n", buffer + wcslen(input), next);
next = NULL;
wcscpy(buffer, L"");
token = wcstok(buffer, L"=", &next);
ok(token == NULL, "expected NULL, got \"%ls\"\n", token);
ok(next == buffer, "expected %p, got %p\n", buffer, next);
token = wcstok(NULL, L"=", &next);
ok(!token, "expected NULL, got \"%ls\"\n", token);
ok(next == buffer, "expected %p, got %p\n", buffer, next);
}
static void test__strnicmp(void)
{
static const char str1[] = "TEST";
static const char str2[] = "test";
int ret;
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _strnicmp(str1, NULL, 2);
CHECK_CALLED(invalid_parameter_handler);
ok(ret == _NLSCMPERROR, "got %d.\n", ret);
ok(errno == EINVAL, "Unexpected errno %d.\n", errno);
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _strnicmp(str1, str2, -1);
CHECK_CALLED(invalid_parameter_handler);
ok(ret == _NLSCMPERROR, "got %d.\n", ret);
ok(errno == EINVAL, "Unexpected errno %d.\n", errno);
ret = _strnicmp(str1, str2, 0);
ok(!ret, "got %d.\n", ret);
ret = _strnicmp(str1, str2, 0x7fffffff);
ok(!ret, "got %d.\n", ret);
/* If numbers of characters to compare is too big return error */
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
ret = _strnicmp(str1, str2, 0x80000000);
CHECK_CALLED(invalid_parameter_handler);
ok(ret == _NLSCMPERROR, "got %d.\n", ret);
ok(errno == EINVAL, "Unexpected errno %d.\n", errno);
}
static void test_wcsnicmp(void)
{
static const wchar_t str1[] = L"TEST";
static const wchar_t str2[] = L"test";
int ret;
errno = 0xdeadbeef;
ret = wcsnicmp(str1, str2, -1);
ok(!ret, "got %d.\n", ret);
ret = wcsnicmp(str1, str2, 0x7fffffff);
ok(!ret, "got %d.\n", ret);
}
static void test_SpecialCasing(void)
{
int i;
wint_t ret, exp;
_locale_t locale;
struct test {
const char *lang;
wint_t ch;
wint_t exp;
};
struct test ucases[] = {
{"English", 'I', 'i'}, /* LATIN CAPITAL LETTER I */
{"English", 0x0130}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
{"Turkish", 'I', 'i'}, /* LATIN CAPITAL LETTER I */
{"Turkish", 0x0130}, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
};
struct test lcases[] = {
{"English", 'i', 'I'}, /* LATIN SMALL LETTER I */
{"English", 0x0131}, /* LATIN SMALL LETTER DOTLESS I */
{"Turkish", 'i', 'I'}, /* LATIN SMALL LETTER I */
{"Turkish", 0x0131}, /* LATIN SMALL LETTER DOTLESS I */
};
for (i = 0; i < ARRAY_SIZE(ucases); i++) {
if (!setlocale(LC_ALL, ucases[i].lang)) {
win_skip("skipping special case tests for %s\n", ucases[i].lang);
continue;
}
ret = towlower(ucases[i].ch);
exp = ucases[i].exp ? ucases[i].exp : ucases[i].ch;
ok(ret == exp, "expected lowercase %x, got %x for locale %s\n", exp, ret, ucases[i].lang);
}
for (i = 0; i < ARRAY_SIZE(lcases); i++) {
if (!setlocale(LC_ALL, lcases[i].lang)) {
win_skip("skipping special case tests for %s\n", lcases[i].lang);
continue;
}
ret = towupper(lcases[i].ch);
exp = lcases[i].exp ? lcases[i].exp : lcases[i].ch;
ok(ret == exp, "expected uppercase %x, got %x for locale %s\n", exp, ret, lcases[i].lang);
}
setlocale(LC_ALL, "C");
/* test _towlower_l creating locale */
for (i = 0; i < ARRAY_SIZE(ucases); i++) {
if (!(locale = _create_locale(LC_ALL, ucases[i].lang))) {
win_skip("locale %s not available. skipping\n", ucases[i].lang);
continue;
}
ret = _towlower_l(ucases[i].ch, locale);
exp = ucases[i].exp ? ucases[i].exp : ucases[i].ch;
ok(ret == exp, "expected lowercase %x, got %x for locale %s\n", exp, ret, ucases[i].lang);
_free_locale(locale);
}
/* test _towupper_l creating locale */
for (i = 0; i < ARRAY_SIZE(lcases); i++) {
if (!(locale = _create_locale(LC_ALL, lcases[i].lang))) {
win_skip("locale %s not available. skipping\n", lcases[i].lang);
continue;
}
ret = _towupper_l(lcases[i].ch, locale);
exp = lcases[i].exp ? lcases[i].exp : lcases[i].ch;
ok(ret == exp, "expected uppercase %x, got %x for locale %s\n", exp, ret, lcases[i].lang);
_free_locale(locale);
}
}
static void test__mbbtype_l(void)
{
int expected, ret;
unsigned int c;
_setmbcp(_MB_CP_LOCALE);
for (c = 0; c < 256; ++c)
{
expected = _mbbtype(c, 0);
ret = _mbbtype_l(c, 0, NULL);
ok(ret == expected, "c %#x, got ret %#x, expected %#x.\n", c, ret, expected);
expected = _mbbtype(c, 1);
ret = _mbbtype_l(c, 1, NULL);
ok(ret == expected, "c %#x, got ret %#x, expected %#x.\n", c, ret, expected);
}
}
static void test_strcmp(void)
{
int ret = strcmp( "abc", "abcd" );
ok( ret == -1, "wrong ret %d\n", ret );
ret = strcmp( "", "abc" );
ok( ret == -1, "wrong ret %d\n", ret );
ret = strcmp( "abc", "ab\xa0" );
ok( ret == -1, "wrong ret %d\n", ret );
ret = strcmp( "ab\xb0", "ab\xa0" );
ok( ret == 1, "wrong ret %d\n", ret );
ret = strcmp( "ab\xc2", "ab\xc2" );
ok( ret == 0, "wrong ret %d\n", ret );
ret = strncmp( "abc", "abcd", 3 );
ok( ret == 0, "wrong ret %d\n", ret );
ret = strncmp( "", "abc", 3 );
ok( ret == -1, "wrong ret %d\n", ret );
ret = strncmp( "abc", "ab\xa0", 4 );
ok( ret == -1, "wrong ret %d\n", ret );
ret = strncmp( "ab\xb0", "ab\xa0", 3 );
ok( ret == 1, "wrong ret %d\n", ret );
ret = strncmp( "ab\xb0", "ab\xa0", 2 );
ok( ret == 0, "wrong ret %d\n", ret );
ret = strncmp( "ab\xc2", "ab\xc2", 3 );
ok( ret == 0, "wrong ret %d\n", ret );
ret = strncmp( "abc", "abd", 0 );
ok( ret == 0, "wrong ret %d\n", ret );
ret = strncmp( "abc", "abc", 12 );
ok( ret == 0, "wrong ret %d\n", ret );
}
#define expect_bin(buf, value, len) { ok(memcmp((buf), value, len) == 0, \
"Binary buffer mismatch - expected %s, got %s\n", \
debugstr_an(value, len), debugstr_an((char *)(buf), len)); }
static void test__mbsncpy_s(void)
{
unsigned char *mbstring = (unsigned char *)"\xb0\xb1\xb2\xb3Q\xb4\xb5\x0";
unsigned char *mbstring2 = (unsigned char *)"\xb0\x0";
unsigned char buf[16];
errno_t err;
int oldcp;
oldcp = _getmbcp();
if (_setmbcp(936))
{
skip("Code page 936 is not available, skipping test.\n");
return;
}
errno = 0xdeadbeef;
memset(buf, 0xcc, sizeof(buf));
err = _mbsncpy_s(NULL, 0, mbstring, 0);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
errno = 0xdeadbeef;
memset(buf, 0xcc, sizeof(buf));
err = _mbsncpy_s(buf, 6, mbstring, 1);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
expect_bin(buf, "\xb0\xb1\0\xcc", 4);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 6, mbstring, 2);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
expect_bin(buf, "\xb0\xb1\xb2\xb3\0\xcc", 6);
errno = 0xdeadbeef;
memset(buf, 0xcc, sizeof(buf));
err = _mbsncpy_s(buf, 2, mbstring, _TRUNCATE);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(err == STRUNCATE, "got %d.\n", err);
expect_bin(buf, "\x00\xb1\xcc", 3);
memset(buf, 0xcc, sizeof(buf));
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 2, mbstring, 1);
ok(errno == err, "got %d.\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ok(err == ERANGE, "got %d.\n", err);
expect_bin(buf, "\x0\xcc\xcc", 3);
memset(buf, 0xcc, sizeof(buf));
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 2, mbstring, 3);
ok(errno == err, "got %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ok(err == ERANGE, "got %d.\n", err);
expect_bin(buf, "\x0\xcc\xcc", 3);
memset(buf, 0xcc, sizeof(buf));
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 1, mbstring, 3);
ok(errno == err, "got %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ok(err == ERANGE, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 0, mbstring, 3);
ok(errno == err, "got %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ok(err == EINVAL, "got %d.\n", err);
expect_bin(buf, "\xcc", 1);
memset(buf, 0xcc, sizeof(buf));
SET_EXPECT(invalid_parameter_handler);
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 0, mbstring, 0);
ok(errno == err, "got %d\n", errno);
CHECK_CALLED(invalid_parameter_handler);
ok(err == EINVAL, "got %d.\n", err);
expect_bin(buf, "\xcc", 1);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, -1, mbstring, 0);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, -1, mbstring, 256);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
expect_bin(buf, "\xb0\xb1\xb2\xb3Q\xb4\xb5\x0\xcc", 9);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 1, mbstring2, 4);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 2, mbstring2, 4);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 1, mbstring2, _TRUNCATE);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(err == STRUNCATE, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 2, mbstring2, _TRUNCATE);
ok(errno == 0xdeadbeef, "got %d\n", errno);
ok(!err, "got %d.\n", err);
expect_bin(buf, "\xb0\x0\xcc", 3);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 1, mbstring2, 1);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 2, mbstring2, 1);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 3, mbstring2, 1);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
memset(buf, 0xcc, sizeof(buf));
errno = 0xdeadbeef;
err = _mbsncpy_s(buf, 3, mbstring2, 2);
ok(errno == err, "got %d\n", errno);
ok(err == EILSEQ, "got %d.\n", err);
expect_bin(buf, "\x0\xcc", 2);
_setmbcp(oldcp);
}
static void test_mbstowcs(void)
{
static const char mbs[] = { 0xc3, 0xa9, 0 };
WCHAR wcs[2];
size_t ret;
if (!setlocale(LC_ALL, "en_US.UTF-8"))
{
win_skip("skipping UTF8 mbstowcs tests\n");
return;
}
ret = mbstowcs(NULL, mbs, 0);
ok(ret == 1, "mbstowcs returned %Id\n", ret);
memset(wcs, 0xfe, sizeof(wcs));
ret = mbstowcs(wcs, mbs, 1);
ok(ret == 1, "mbstowcs returned %Id\n", ret);
ok(wcs[0] == 0xe9, "wcsstring[0] = %x\n", wcs[0]);
ok(wcs[1] == 0xfefe, "wcsstring[1] = %x\n", wcs[1]);
setlocale(LC_ALL, "C");
}
START_TEST(string)
{
ok(_set_invalid_parameter_handler(test_invalid_parameter_handler) == NULL,
"Invalid parameter handler was already set\n");
test_strtod();
test_strtof();
test__memicmp();
test__memicmp_l();
test___strncnt();
test_C_locale();
test_mbsspn();
test_wcstok();
test__strnicmp();
test_wcsnicmp();
test_SpecialCasing();
test__mbbtype_l();
test_strcmp();
test__mbsncpy_s();
test_mbstowcs();
}

View file

@ -0,0 +1,184 @@
/*
* Copyright 2021 Arkadiusz Hiler for CodeWeavers
*
* 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
*/
#include <errno.h>
#include <stdarg.h>
#include <process.h>
#include <windef.h>
#include <winbase.h>
#include "wine/test.h"
#include "threaddll.h"
enum beginthread_method
{
use_beginthread,
use_beginthreadex
};
static char *get_thread_dll_path(void)
{
static char path[MAX_PATH];
const char dll_name[] = "threaddll.dll";
DWORD written;
HANDLE file;
HRSRC res;
void *ptr;
GetTempPathA(ARRAY_SIZE(path), path);
strcat(path, dll_name);
file = CreateFileA(path, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s: %lu.\n",
debugstr_a(path), GetLastError());
res = FindResourceA(NULL, dll_name, "TESTDLL");
ok(!!res, "Failed to load resource: %lu\n", GetLastError());
ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL);
ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource\n");
CloseHandle(file);
return path;
}
static void set_thead_dll_detach_event(HANDLE dll, HANDLE event)
{
void (WINAPI *_set_detach_event)(HANDLE event);
_set_detach_event = (void*) GetProcAddress(dll, "set_detach_event");
ok(_set_detach_event != NULL, "Failed to get set_detach_event: %lu\n", GetLastError());
_set_detach_event(event);
}
static void test_thread_library_reference(char *thread_dll,
enum beginthread_method beginthread_method,
enum thread_exit_method exit_method)
{
HANDLE detach_event;
HMODULE dll;
DWORD ret;
uintptr_t thread_handle;
struct threaddll_args args;
args.exit_method = exit_method;
detach_event = CreateEventA(NULL, FALSE, FALSE, NULL);
ok(detach_event != NULL, "Failed to create an event: %lu\n", GetLastError());
args.confirm_running = CreateEventA(NULL, FALSE, FALSE, NULL);
ok(args.confirm_running != NULL, "Failed to create an event: %lu\n", GetLastError());
args.past_free = CreateEventA(NULL, FALSE, FALSE, NULL);
ok(args.past_free != NULL, "Failed to create an event: %lu\n", GetLastError());
dll = LoadLibraryA(thread_dll);
ok(dll != NULL, "Failed to load the test dll: %lu\n", GetLastError());
set_thead_dll_detach_event(dll, detach_event);
if (beginthread_method == use_beginthreadex)
{
_beginthreadex_start_routine_t proc = (void*) GetProcAddress(dll, "stdcall_thread_proc");
ok(proc != NULL, "Failed to get stdcall_thread_proc: %lu\n", GetLastError());
thread_handle = _beginthreadex(NULL, 0, proc, &args, 0, NULL);
}
else
{
_beginthread_start_routine_t proc = (void*) GetProcAddress(dll, "cdecl_thread_proc");
ok(proc != NULL, "Failed to get stdcall_thread_proc: %lu\n", GetLastError());
thread_handle = _beginthread(proc, 0, &args);
}
ok(thread_handle != -1 && thread_handle != 0, "Failed to begin thread: %u\n", errno);
ret = FreeLibrary(dll);
ok(ret, "Failed to free the library: %lu\n", GetLastError());
ret = WaitForSingleObject(args.confirm_running, 200);
ok(ret == WAIT_OBJECT_0, "Event was not signaled, ret: %lu, err: %lu\n", ret, GetLastError());
ret = WaitForSingleObject(detach_event, 0);
ok(ret == WAIT_TIMEOUT, "Thread detach happened unexpectedly signaling an event, ret: %ld, err: %lu\n", ret, GetLastError());
ret = SetEvent(args.past_free);
ok(ret, "Failed to signal event: %ld\n", GetLastError());
if (beginthread_method == use_beginthreadex)
{
ret = WaitForSingleObject((HANDLE)thread_handle, 200);
ok(ret == WAIT_OBJECT_0, "Thread has not exited, ret: %ld, err: %lu\n", ret, GetLastError());
}
ret = WaitForSingleObject(detach_event, 200);
ok(ret == WAIT_OBJECT_0, "Detach event was not signaled, ret: %ld, err: %lu\n", ret, GetLastError());
if (beginthread_method == use_beginthreadex)
CloseHandle((HANDLE)thread_handle);
CloseHandle(args.past_free);
CloseHandle(args.confirm_running);
CloseHandle(detach_event);
}
static BOOL handler_called;
void CDECL test_invalid_parameter_handler(const wchar_t *expression,
const wchar_t *function_name,
const wchar_t *file_name,
unsigned line_number,
uintptr_t reserved)
{
handler_called = TRUE;
}
static void test_thread_invalid_params(void)
{
uintptr_t hThread;
_invalid_parameter_handler old = _set_invalid_parameter_handler(test_invalid_parameter_handler);
errno = 0;
handler_called = FALSE;
hThread = _beginthreadex(NULL, 0, NULL, NULL, 0, NULL);
ok(hThread == 0, "_beginthreadex unexpected ret: %Iu\n", hThread);
ok(errno == EINVAL, "_beginthreadex unexpected errno: %d\n", errno);
ok(handler_called, "Expected invalid_parameter_handler to be called\n");
errno = 0;
handler_called = FALSE;
hThread = _beginthread(NULL, 0, NULL);
ok(hThread == -1, "_beginthread unexpected ret: %Iu\n", hThread);
ok(errno == EINVAL, "_beginthread unexpected errno: %d\n", errno);
ok(handler_called, "Expected invalid_parameter_handler to be called\n");
_set_invalid_parameter_handler(old);
}
START_TEST(thread)
{
BOOL ret;
char *thread_dll = get_thread_dll_path();
test_thread_library_reference(thread_dll, use_beginthread, thread_exit_return);
test_thread_library_reference(thread_dll, use_beginthread, thread_exit_endthread);
test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_return);
test_thread_library_reference(thread_dll, use_beginthreadex, thread_exit_endthreadex);
ret = DeleteFileA(thread_dll);
ok(ret, "Failed to remove the test dll, err: %lu\n", GetLastError());
test_thread_invalid_params();
}

View file

@ -0,0 +1,71 @@
/*
* Copyright 2021 Arkadiusz Hiler for CodeWeavers
*
* 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
*/
#if 0
#pragma makedep testdll
#endif
#include <windows.h>
#include "threaddll.h"
static HANDLE detach_event;
void CDECL _endthread(void);
void CDECL _endthreadex(unsigned int);
void WINAPI set_detach_event(HANDLE event)
{
detach_event = event;
}
static unsigned internal_thread_proc(void *param)
{
struct threaddll_args *args = param;
SetEvent(args->confirm_running);
WaitForSingleObject(args->past_free, INFINITE);
if (args->exit_method == thread_exit_endthread)
_endthread();
else if (args->exit_method == thread_exit_endthreadex)
_endthreadex(0);
return 0;
}
unsigned WINAPI stdcall_thread_proc(void *param)
{
return internal_thread_proc(param);
}
void CDECL cdecl_thread_proc(void *param)
{
internal_thread_proc(param);
}
BOOL WINAPI DllMain(HINSTANCE instance_new, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_DETACH:
if (detach_event) SetEvent(detach_event);
break;
}
return TRUE;
}

View file

@ -0,0 +1,30 @@
/*
* Copyright 2021 Arkadiusz Hiler for CodeWeavers
*
* 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
*/
enum thread_exit_method {
thread_exit_return,
thread_exit_endthread,
thread_exit_endthreadex
};
struct threaddll_args
{
HANDLE confirm_running;
HANDLE past_free;
enum thread_exit_method exit_method;
};

View file

@ -0,0 +1,3 @@
@ stdcall set_detach_event(ptr)
@ stdcall stdcall_thread_proc(ptr)
@ cdecl cdecl_thread_proc(ptr)