From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- dlls/ucrtbase/tests/Makefile.in | 3 +- dlls/ucrtbase/tests/file.c | 160 ++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 dlls/ucrtbase/tests/file.c
diff --git a/dlls/ucrtbase/tests/Makefile.in b/dlls/ucrtbase/tests/Makefile.in index cd01a3aacb9..d6ea94bc70f 100644 --- a/dlls/ucrtbase/tests/Makefile.in +++ b/dlls/ucrtbase/tests/Makefile.in @@ -1,10 +1,11 @@ TESTDLL = ucrtbase.dll -IMPORTS = ucrtbase +IMPORTS = ucrtbase version EXTRADEFS = -fno-builtin
SOURCES = \ cpp.c \ environ.c \ + file.c \ misc.c \ printf.c \ scanf.c \ diff --git a/dlls/ucrtbase/tests/file.c b/dlls/ucrtbase/tests/file.c new file mode 100644 index 00000000000..69e51133528 --- /dev/null +++ b/dlls/ucrtbase/tests/file.c @@ -0,0 +1,160 @@ +/* + * 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 "wine/test.h" +#include <winver.h> + +static BOOL detect_too_old_ucrtbase( void ) +{ + DWORD size; + BYTE *buffer; + VS_FIXEDFILEINFO *info; + UINT size_info; + BOOL ret = FALSE; + + if ((size = GetFileVersionInfoSizeW( L"ucrtbase.dll", NULL )) && (buffer = malloc( size ))) + { + if (GetFileVersionInfoW( L"ucrtbase.dll", 0, size, buffer ) && + VerQueryValueW( buffer, L"\", (LPVOID*)&info, &size_info ) ) + { + /* last known not supporting version is 10.0.17763.719 */ + /* first known supporting version is 10.0.19041.789 */ + ret = (info->dwProductVersionMS == (10u << 16)) && info->dwProductVersionLS < ((19041 << 16) | 789); + } + free( buffer ); + } + return ret; +} + +static void test_std_buffer_state( const char *selfname ) +{ + char cmdline[MAX_PATH]; + STARTUPINFOA startup = {.cb = sizeof(startup), .dwFlags = 0}; + PROCESS_INFORMATION proc; + DWORD exit_code; + BOOL ret; + + /* Win7, 8 and early Win10 keep stderr bufferred on files */ + if (!winetest_platform_is_wine && detect_too_old_ucrtbase()) + { + skip("Too old ucrtbase version\n"); + return; + } + + /* use child process to temper with std streams and only use ok() in parent process */ + sprintf( cmdline, "%s file stdbuf", selfname); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &proc ); + ok(ret, "Couldn't start process '%s'\n", cmdline); + ret = WaitForSingleObject( proc.hProcess, 30000 ); + ok(ret == WAIT_OBJECT_0, "Unexpected process termination\n"); + ret = GetExitCodeProcess( proc.hProcess, &exit_code ); + ok( ret, "Unexpected result\n" ); + todo_wine + ok( exit_code == 3, "Unexpected exit code %lx\n", exit_code); + CloseHandle( proc.hProcess ); + CloseHandle( proc.hThread ); +} + +static BOOL create_file( const char *filename, const char *content ) +{ + FILE *file; + int len; + + if (!(file = fopen( filename, "wb" ))) return FALSE; + len = fputs( content, file ); + fclose( file ); + return len >= 0; +} + +static unsigned gather_buffer_state( FILE *f ) +{ + unsigned ret = 0; + int fd = _fileno( f ); + __int64 pos; + char ch; + + /* these operations shall force buffer creation (if required) */ + switch (fd) + { + case 0: + if (fscanf( f, "%c", &ch ) != 1 || (pos = _telli64( fd )) < 0) + ret |= 1u << 7; + else if (pos > 2) + ret |= 1u << fd; + break; + case 1: + case 2: + if (fprintf( f, "a" ) != 1 || (pos = _telli64( fd )) < 0) + ret |= 1u << 7; + else if (pos == 0) + ret |= 1u << fd; + break; + default: + ret |= 1u << 7; + break; + } + return ret; +} + +static void test_std_buffer_state_child( int argc, char **argv ) +{ + unsigned exit_code = 0; + + /* return: + * - bit7 set in case of error; + * - bit0,1,2 set if fd 0,1,2 is buffered + */ + if (!create_file( "file0.tmp", "content\n" ) || + !freopen( "file0.tmp", "r", stdin ) || + !freopen( "file1.tmp", "w", stdout ) || + !freopen( "file2.tmp", "w", stderr )) + exit_code |= 1u << 7; + + exit_code |= gather_buffer_state( stdin ); + exit_code |= gather_buffer_state( stdout ); + exit_code |= gather_buffer_state( stderr ); + + fclose( stdin ); + fclose( stdout ); + fclose( stderr ); + + if (!DeleteFileA( "file0.tmp" ) || !DeleteFileA( "file1.tmp" ) || !DeleteFileA( "file2.tmp" )) + exit_code |= 1u << 7; + + ExitProcess( exit_code ); +} + +START_TEST( file ) +{ + int arg_c; + char** arg_v; + + arg_c = winetest_get_mainargs( &arg_v ); + + if (arg_c >= 3) + { + if (strcmp( arg_v[2], "stdbuf" ) == 0) + test_std_buffer_state_child( arg_c, arg_v ); + else + ok( 0, "invalid argument '%s'\n", arg_v[2] ); + return; + } + test_std_buffer_state( arg_v[0] ); +}