Reactos
at master 477 lines 16 kB view raw
1/* 2 * Unit test suite for file functions 3 * 4 * Copyright 2024 Eric Pouech for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21#include <errno.h> 22#include <direct.h> 23#include <stdarg.h> 24#include <locale.h> 25#include <process.h> 26#include <share.h> 27#include <sys/stat.h> 28 29#include <windef.h> 30#include <winbase.h> 31#include <winnls.h> 32#include "wine/test.h" 33#include <corecrt_io.h> 34 35#define STDIN_FILENO 0 36#define STDOUT_FILENO 1 37#define STDERR_FILENO 2 38 39static void test_std_stream_buffering(void) 40{ 41 int dup_fd, ret, pos; 42 FILE *file; 43 char ch; 44 45 dup_fd = _dup(STDOUT_FILENO); 46 ok(dup_fd != -1, "_dup failed\n"); 47 48 file = freopen("std_stream_test.tmp", "w", stdout); 49 ok(file != NULL, "freopen failed\n"); 50 51 ret = fprintf(stdout, "test"); 52 pos = _telli64(STDOUT_FILENO); 53 54 fflush(stdout); 55 _dup2(dup_fd, STDOUT_FILENO); 56 close(dup_fd); 57 setvbuf(stdout, NULL, _IONBF, 0); 58 59 ok(ret == 4, "fprintf(stdout) returned %d\n", ret); 60 ok(!pos, "expected stdout to be buffered\n"); 61 62 dup_fd = _dup(STDERR_FILENO); 63 ok(dup_fd != -1, "_dup failed\n"); 64 65 file = freopen("std_stream_test.tmp", "w", stderr); 66 ok(file != NULL, "freopen failed\n"); 67 68 ret = fprintf(stderr, "test"); 69 ok(ret == 4, "fprintf(stderr) returned %d\n", ret); 70 pos = _telli64(STDERR_FILENO); 71 if (broken(!GetProcAddress(GetModuleHandleA("ucrtbase"), "__CxxFrameHandler4") && !pos)) 72 trace("stderr is buffered\n"); 73 else 74 ok(pos == 4, "expected stderr to be unbuffered (%d)\n", pos); 75 76 fflush(stderr); 77 _dup2(dup_fd, STDERR_FILENO); 78 close(dup_fd); 79 80 dup_fd = _dup(STDIN_FILENO); 81 ok(dup_fd != -1, "_dup failed\n"); 82 83 file = freopen("std_stream_test.tmp", "r", stdin); 84 ok(file != NULL, "freopen failed\n"); 85 86 ch = 0; 87 ret = fscanf(stdin, "%c", &ch); 88 ok(ret == 1, "fscanf returned %d\n", ret); 89 ok(ch == 't', "ch = 0x%x\n", (unsigned char)ch); 90 pos = _telli64(STDIN_FILENO); 91 ok(pos == 4, "pos = %d\n", pos); 92 93 fflush(stdin); 94 _dup2(dup_fd, STDIN_FILENO); 95 close(dup_fd); 96 97 ok(DeleteFileA("std_stream_test.tmp"), "DeleteFile failed\n"); 98} 99 100int CDECL _get_stream_buffer_pointers(FILE*,char***,char***,int**); 101static void test_iobuf_layout(void) 102{ 103 union 104 { 105 FILE *f; 106 struct 107 { 108 char* _ptr; 109 char* _base; 110 int _cnt; 111 int _flag; 112 int _file; 113 int _charbuf; 114 int _bufsiz; 115 char* _tmpfname; 116 CRITICAL_SECTION _crit; 117 } *iobuf; 118 } fp; 119 char *tempf, *ptr, **file_ptr, **file_base; 120 int cnt, r, *file_cnt; 121 122 tempf = _tempnam(".","wne"); 123 fp.f = fopen(tempf, "wb"); 124 ok(fp.f != NULL, "fopen failed with error: %d\n", errno); 125 126 ok(!(fp.iobuf->_flag & 0x440), "fp.iobuf->_flag = %x\n", fp.iobuf->_flag); 127 r = fprintf(fp.f, "%s", "init"); 128 ok(r == 4, "fprintf returned %d\n", r); 129 ok(fp.iobuf->_flag & 0x40, "fp.iobuf->_flag = %x\n", fp.iobuf->_flag); 130 ok(fp.iobuf->_cnt + 4 == fp.iobuf->_bufsiz, "_cnt = %d, _bufsiz = %d\n", 131 fp.iobuf->_cnt, fp.iobuf->_bufsiz); 132 133 ptr = fp.iobuf->_ptr; 134 cnt = fp.iobuf->_cnt; 135 r = fprintf(fp.f, "%s", "hello"); 136 ok(r == 5, "fprintf returned %d\n", r); 137 ok(ptr + 5 == fp.iobuf->_ptr, "fp.iobuf->_ptr = %p, expected %p\n", fp.iobuf->_ptr, ptr + 5); 138 ok(cnt - 5 == fp.iobuf->_cnt, "fp.iobuf->_cnt = %d, expected %d\n", fp.iobuf->_cnt, cnt - 5); 139 ok(fp.iobuf->_ptr + fp.iobuf->_cnt == fp.iobuf->_base + fp.iobuf->_bufsiz, 140 "_ptr = %p, _cnt = %d, _base = %p, _bufsiz = %d\n", 141 fp.iobuf->_ptr, fp.iobuf->_cnt, fp.iobuf->_base, fp.iobuf->_bufsiz); 142 143 _get_stream_buffer_pointers(fp.f, &file_base, &file_ptr, &file_cnt); 144 ok(file_base == &fp.iobuf->_base, "_base = %p, expected %p\n", file_base, &fp.iobuf->_base); 145 ok(file_ptr == &fp.iobuf->_ptr, "_ptr = %p, expected %p\n", file_ptr, &fp.iobuf->_ptr); 146 ok(file_cnt == &fp.iobuf->_cnt, "_cnt = %p, expected %p\n", file_cnt, &fp.iobuf->_cnt); 147 148 r = setvbuf(fp.f, NULL, _IONBF, 0); 149 ok(!r, "setvbuf returned %d\n", r); 150 ok(fp.iobuf->_flag & 0x400, "fp.iobuf->_flag = %x\n", fp.iobuf->_flag); 151 152 ok(TryEnterCriticalSection(&fp.iobuf->_crit), "TryEnterCriticalSection section returned FALSE\n"); 153 LeaveCriticalSection(&fp.iobuf->_crit); 154 155 fclose(fp.f); 156 unlink(tempf); 157} 158 159static void test_std_stream_open(void) 160{ 161 FILE *f; 162 int fd; 163 164 fd = _dup(STDIN_FILENO); 165 ok(fd != -1, "_dup failed\n"); 166 167 ok(!fclose(stdin), "fclose failed\n"); 168 f = fopen("nul", "r"); 169 ok(f != stdin, "f = %p, stdin = %p\n", f, stdin); 170 ok(_fileno(f) == STDIN_FILENO, "_fileno(f) = %d\n", _fileno(f)); 171 ok(!fclose(f), "fclose failed\n"); 172 173 f = freopen("nul", "r", stdin); 174 ok(f == stdin, "f = %p, expected %p\n", f, stdin); 175 ok(_fileno(f) == STDIN_FILENO, "_fileno(f) = %d\n", _fileno(f)); 176 177 _dup2(fd, STDIN_FILENO); 178 close(fd); 179} 180 181static void test_fopen(void) 182{ 183 int i; 184 FILE *f; 185 wchar_t wpath[MAX_PATH]; 186 static const struct { 187 const char *loc; 188 const char *path; 189 } tests[] = { 190 { "German.utf8", "t\xc3\xa4\xc3\x8f\xc3\xb6\xc3\x9f.txt" }, 191 { "Polish.utf8", "t\xc4\x99\xc5\x9b\xc4\x87.txt" }, 192 { "Turkish.utf8", "t\xc3\x87\xc4\x9e\xc4\xb1\xc4\xb0\xc5\x9e.txt" }, 193 { "Arabic.utf8", "t\xd8\xaa\xda\x86.txt" }, 194 { "Japanese.utf8", "t\xe3\x82\xaf\xe3\x83\xa4.txt" }, 195 { "Chinese.utf8", "t\xe4\xb8\x82\xe9\xbd\xab.txt" }, 196 { "Japanese", "t\xb8\xd5.txt" }, 197 198 }; 199 200 for(i=0; i<ARRAY_SIZE(tests); i++) { 201 if(!setlocale(LC_ALL, tests[i].loc)) { 202 win_skip("skipping locale %s\n", tests[i].loc); 203 continue; 204 } 205 206 if(!MultiByteToWideChar(___lc_codepage_func() == CP_UTF8 ? CP_UTF8 : CP_ACP, 207 MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, tests[i].path, -1, wpath, MAX_PATH)) 208 continue; 209 210 f = _fsopen(tests[i].path, "w", SH_DENYNO); 211 ok(!!f, "failed to create %s with locale %s\n", 212 debugstr_a(tests[i].path), tests[i].loc); 213 fclose(f); 214 215 f = _wfsopen(wpath, L"r", SH_DENYNO); 216 ok(!!f, "failed to open %s with locale %s\n", 217 debugstr_a(tests[i].path), tests[i].loc); 218 if(f) fclose(f); 219 220 ok(!unlink(tests[i].path), "failed to unlink %s with locale %s\n", 221 tests[i].path, tests[i].loc); 222 } 223 setlocale(LC_ALL, "C"); 224} 225 226static void test_utf8(const char *argv0) 227{ 228 const char file[] = "file\xc4\x99\xc5\x9b\xc4\x87.a"; 229 const char dir[] = "dir\xc4\x99\xc5\x9b\xc4\x87"; 230 const WCHAR fileW[] = L"file\x0119\x015b\x0107.a"; 231 const WCHAR dirW[] = L"dir\x0119\x015b\x0107"; 232 233 char file2[32], buf[256], *p, *q, *env[2]; 234 struct _finddata64i32_t fdata64i32; 235 struct _finddata32_t fdata32; 236 struct _finddata64_t fdata64; 237 intptr_t hfind, hproc; 238 WCHAR bufW[256], *pW; 239 struct _stat64 stat; 240 FILE *f; 241 int ret; 242 243 if (!setlocale(LC_ALL, ".utf8")) 244 { 245 win_skip("utf-8 tests\n"); 246 return; 247 } 248 249 ret = _mkdir(dir); 250 if (ret == -1 && errno == ENOENT) 251 { 252 skip("can't create test environment\n"); 253 return; 254 } 255 ok(!ret, "_mkdir returned %d, error %d\n", ret, errno); 256 257 ret = _chdir(dir); 258 ok(!ret, "_chdir returned %d, error %d\n", ret, errno); 259 260 p = _getcwd(buf, sizeof(buf)); 261 ok(p == buf, "_getcwd returned %p, errno %d\n", p, errno); 262 p = strrchr(p, '\\'); 263 ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf)); 264 ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf)); 265 266 p = _getdcwd(_getdrive(), buf, sizeof(buf)); 267 ok(p == buf, "_getdcwd returned %p, errno %d\n", p, errno); 268 p = strrchr(p, '\\'); 269 ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf)); 270 ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf)); 271 272 p = _fullpath(buf, NULL, sizeof(buf)); 273 ok(p == buf, "_fulpath returned %p, errno %d\n", p, errno); 274 p = strrchr(p, '\\'); 275 ok(!!p, "strrchr returned NULL, buf = %s\n", debugstr_a(buf)); 276 ok(!strcmp(p + 1, dir), "unexpected working directory: %s\n", debugstr_a(buf)); 277 278 f = fopen(file, "w"); 279 ok(!!f, "fopen returned %d, error %d\n", ret, errno); 280 fclose(f); 281 282 ret = access(file, 0); 283 ok(!ret, "access returned %d, error %d\n", ret, errno); 284 285 ret = _stat64(file, &stat); 286 ok(!ret, "_stat64 returned %d, error %d\n", ret, errno); 287 288 ret = _chmod(file, _S_IREAD | _S_IWRITE); 289 ok(!ret, "_chmod returned %d, error %d\n", ret, errno); 290 291 strcpy(file2, file); 292 strcat(file2, "XXXXXX"); 293 p = _mktemp(file2); 294 ok(p == file2, "_mktemp returned %p, file2 %p, errno %d\n", p, file2, errno); 295 ok(!memcmp(file2, file, sizeof(file) - 1), "file2 = %s\n", debugstr_a(file2)); 296 ok(p[ARRAY_SIZE(file) - 1] == 'a', "p = %s\n", debugstr_a(p)); 297 f = fopen(p, "w"); 298 ok(!!f, "fopen returned %d, error %d\n", ret, errno); 299 fclose(f); 300 301 strcpy(buf, file); 302 strcat(buf, "XXXXXX"); 303 p = _mktemp(buf); 304 ok(p == buf, "_mktemp returned %p, buf %p, errno %d\n", p, buf, errno); 305 ok(!memcmp(buf, file, sizeof(file) - 1), "buf = %s\n", debugstr_a(buf)); 306 ok(p[ARRAY_SIZE(file) - 1] == 'b', "p = %s\n", debugstr_a(p)); 307 308 strcpy(buf, file); 309 strcat(buf, "XXXXXX"); 310 ret = _mktemp_s(buf, sizeof(buf)); 311 ok(!memcmp(buf, file, sizeof(file) - 1), "buf = %s\n", debugstr_a(buf)); 312 ok(buf[ARRAY_SIZE(file) - 1] == 'b', "buf = %s\n", debugstr_a(buf)); 313 314 strcpy(buf, file); 315 strcat(buf, "*"); 316 fdata32.name[0] = 'x'; 317 hfind = _findfirst32(buf, &fdata32); 318 ok(hfind != -1, "_findfirst32 returned %Id, errno %d\n", hfind, errno); 319 ok(!memcmp(file, fdata32.name, sizeof(file) - 1), "fdata32.name = %s\n", debugstr_a(fdata32.name)); 320 321 fdata32.name[0] = 'x'; 322 ret = _findnext32(hfind, &fdata32); 323 ok(!ret, "_findnext32 returned %d, errno %d\n", ret, errno); 324 ok(!memcmp(file, fdata32.name, sizeof(file) - 1), "fdata32.name = %s\n", debugstr_a(fdata32.name)); 325 ret = _findclose(hfind); 326 ok(!ret, "_findclose returned %d, errno %d\n", ret, errno); 327 328 329 strcpy(buf, file); 330 strcat(buf, "*"); 331 fdata64.name[0] = 'x'; 332 hfind = _findfirst64(buf, &fdata64); 333 ok(hfind != -1, "_findfirst64 returned %Id, errno %d\n", hfind, errno); 334 ok(!memcmp(file, fdata64.name, sizeof(file) - 1), "fdata64.name = %s\n", debugstr_a(fdata64.name)); 335 336 fdata64.name[0] = 'x'; 337 ret = _findnext64(hfind, &fdata64); 338 ok(!ret, "_findnext64 returned %d, errno %d\n", ret, errno); 339 ok(!memcmp(file, fdata64.name, sizeof(file) - 1), "fdata64.name = %s\n", debugstr_a(fdata64.name)); 340 ret = _findclose(hfind); 341 ok(!ret, "_findclose returned %d, errno %d\n", ret, errno); 342 343 strcpy(buf, file); 344 strcat(buf, "*"); 345 fdata64i32.name[0] = 'x'; 346 hfind = _findfirst64i32(buf, &fdata64i32); 347 ok(hfind != -1, "_findfirst64i32 returned %Id, errno %d\n", hfind, errno); 348 ok(!memcmp(file, fdata64i32.name, sizeof(file) - 1), "fdata64i32.name = %s\n", debugstr_a(fdata64i32.name)); 349 350 fdata64i32.name[0] = 'x'; 351 ret = _findnext64i32(hfind, &fdata64i32); 352 ok(!ret, "_findnext64i32 returned %d, errno %d\n", ret, errno); 353 ok(!memcmp(file, fdata64i32.name, sizeof(file) - 1), "fdata64i32.name = %s\n", debugstr_a(fdata64i32.name)); 354 ret = _findclose(hfind); 355 ok(!ret, "_findclose returned %d, errno %d\n", ret, errno); 356 357 ret = remove(file2); 358 ok(!ret, "remove returned %d, errno %d\n", ret, errno); 359 360 buf[0] = 'x'; 361 _searchenv(file, "env", buf); 362 p = strrchr(buf, '\\'); 363 ok(!!p, "buf = %s\n", debugstr_a(buf)); 364 ok(!strcmp(p + 1, file), "buf = %s\n", debugstr_a(buf)); 365 366 ret = _wunlink(fileW); 367 ok(!ret, "_wunlink returned %d, errno %d\n", ret, errno); 368 369 ret = _chdir(".."); 370 ok(!ret, "_chdir returned %d, error %d\n", ret, errno); 371 372 ret = _wrmdir(dirW); 373 ok(!ret, "_wrmdir returned %d, errno %d\n", ret, errno); 374 375 p = _tempnam(NULL, file); 376 ok(!!p, "_tempnam returned NULL, error %d\n", errno); 377 q = strrchr(p, '\\'); 378 ok(!!q, "_tempnam returned %s\n", debugstr_a(p)); 379 todo_wine ok(!memcmp(q + 1, file, ARRAY_SIZE(file) - 1), 380 "incorrect file prefix: %s\n", debugstr_a(p)); 381 free(p); 382 383 /* native implementation mixes CP_UTF8 and CP_ACP */ 384 if (GetACP() != CP_UTF8) 385 { 386 /* make sure wide environment is initialized (works around bug in native) */ 387 ret = _putenv("__wine_env_test=test"); 388 ok(!ret, "_putenv returned %d, errno %d\n", ret, errno); 389 _wgetenv(L"__wine_env_test"); 390 391 strcpy(buf, file); 392 strcat(buf, "=test"); 393 ret = _putenv(buf); 394 ok(!ret, "_putenv returned %d, errno %d\n", ret, errno); 395 /* bug in native _wgetenv/_putenv implementation */ 396 pW = _wgetenv(fileW); 397 ok(!pW, "environment variable name was converted\n"); 398 bufW[0] = 0; 399 ret = GetEnvironmentVariableW(fileW, bufW, ARRAY_SIZE(bufW)); 400 todo_wine ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError()); 401 todo_wine ok(!wcscmp(bufW, L"test"), "bufW = %s\n", debugstr_w(bufW)); 402 strcpy(buf, file); 403 strcat(buf, "="); 404 ret = _putenv(buf); 405 ok(!ret, "_putenv returned %d, errno %d\n", ret, errno); 406 407 strcpy(buf, "__wine_env_test="); 408 strcat(buf, file); 409 ret = _putenv(buf); 410 ok(!ret, "_putenv returned %d, errno %d\n", ret, errno); 411 /* bug in native _wgetenv/_putenv implementation */ 412 pW = _wgetenv(L"__wine_env_test"); 413 ok(wcscmp(pW, fileW), "pW = %s\n", debugstr_w(pW)); 414 ret = GetEnvironmentVariableW(L"__wine_env_test", bufW, ARRAY_SIZE(bufW)); 415 ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError()); 416 todo_wine ok(!wcscmp(bufW, fileW), "bufW = %s\n", debugstr_w(bufW)); 417 418 wcscpy(bufW, L"__wine_env_test="); 419 wcscat(bufW, fileW); 420 ret = _wputenv(bufW); 421 ok(!ret, "_wputenv returned %d, errno %d\n", ret, errno); 422 p = getenv("__wine_env_test"); 423 ok(strcmp(p, file), "environment variable was converted\n"); 424 strcpy(buf, "__wine_env_test="); 425 ret = _putenv(buf); 426 ok(!ret, "_putenv returned %d, errno %d\n", ret, errno); 427 } 428 429 strcpy(buf, "__wine_env_test="); 430 strcat(buf, file); 431 env[0] = buf; 432 env[1] = NULL; 433 hproc = _spawnle(_P_NOWAIT, argv0, argv0, "file", "utf8", file, NULL, env); 434 ok(hproc != -1, "_spawnl returned %Id, errno %d\n", hproc, errno); 435 wait_child_process((HANDLE)hproc); 436 CloseHandle((HANDLE)hproc); 437 438 setlocale(LC_ALL, "C"); 439} 440 441static void test_utf8_argument(void) 442{ 443 static const WCHAR nameW[] = L"file\x0119\x015b\x0107.a"; 444 const WCHAR *cmdline = GetCommandLineW(), *p; 445 WCHAR buf[256]; 446 DWORD ret; 447 448 p = wcsrchr(cmdline, ' '); 449 ok(!!p, "cmdline = %s\n", debugstr_w(cmdline)); 450 ok(!wcscmp(p + 1, nameW), "cmdline = %s\n", debugstr_w(cmdline)); 451 452 ret = GetEnvironmentVariableW(L"__wine_env_test", buf, ARRAY_SIZE(buf)); 453 ok(ret, "GetEnvironmentVariableW returned error %lu\n", GetLastError()); 454 if (GetACP() == CP_UTF8) 455 ok(!wcscmp(buf, nameW), "__wine_env_test = %s\n", debugstr_w(buf)); 456 else 457 ok(wcscmp(buf, nameW), "environment was converted\n"); 458} 459 460START_TEST(file) 461{ 462 int arg_c; 463 char** arg_v; 464 465 arg_c = winetest_get_mainargs(&arg_v); 466 if(arg_c == 4 && !strcmp(arg_v[2], "utf8")) 467 { 468 test_utf8_argument(); 469 return; 470 } 471 472 test_std_stream_buffering(); 473 test_iobuf_layout(); 474 test_std_stream_open(); 475 test_fopen(); 476 test_utf8(arg_v[0]); 477}