2 * Copyright 2019 Fabian Maurer
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/heap.h"
23 #include "wine/test.h"
25 static void read_all_from_handle(HANDLE handle
, BYTE
**str
, int *len
)
30 BYTE
*ret
= heap_alloc_zero(1);
34 BOOL success
= ReadFile(handle
, buffer
, sizeof(buffer
), &bytes_read
, NULL
);
35 if (!success
|| !bytes_read
)
37 ret
= heap_realloc(ret
, length
+ bytes_read
);
38 memcpy((char *)ret
+ length
, buffer
, bytes_read
);
46 static void write_to_handle(HANDLE handle
, const BYTE
*str
, int len
)
49 WriteFile(handle
, str
, len
, &dummy
, NULL
);
52 static void check_find_output(const BYTE
*child_output
, int child_output_len
, const BYTE
*out_expected
, int out_expected_len
, const char *file
, int line
)
54 BOOL strings_are_equal
;
55 char *child_output_copy
;
56 char *out_expected_copy
;
59 if (child_output_len
!= out_expected_len
)
60 strings_are_equal
= FALSE
;
63 strings_are_equal
= memcmp(child_output
, out_expected
, out_expected_len
) == 0;
66 /* Format strings for debug printing */
67 child_output_copy
= heap_alloc_zero(child_output_len
* 4 + 1);
68 out_expected_copy
= heap_alloc_zero(out_expected_len
* 4 + 1);
70 for (i
= 0, pos
= 0; i
< child_output_len
; i
++)
72 if (child_output
[i
] && child_output
[i
] != '\r' && child_output
[i
] < 128)
73 child_output_copy
[pos
++] = child_output
[i
];
76 sprintf(&child_output_copy
[pos
], "\\x%02x", child_output
[i
]);
81 for (i
= 0, pos
= 0; i
< out_expected_len
; i
++)
83 if (out_expected
[i
] && out_expected
[i
] != '\r' && out_expected
[i
] < 128)
84 out_expected_copy
[pos
++] = out_expected
[i
];
87 sprintf(&out_expected_copy
[pos
], "\\x%02x", out_expected
[i
]);
93 ok_(file
, line
)(strings_are_equal
, "\n#################### Expected:\n"
95 "#################### But got:\n"
97 "####################\n",
98 out_expected_copy
, child_output_copy
);
100 heap_free(child_output_copy
);
101 heap_free(out_expected_copy
);
104 static void mangle_text(const BYTE
*input
, int input_len
, BYTE
*output
, int output_max
, int *output_len
) {
108 /* Check for UTF-16 LE BOM */
109 if (input
[0] == 0xFF && input
[1] == 0xFE)
111 int buffer_count
= 0;
114 /* Copy utf16le into a WCHAR array, stripping the BOM */
115 for (i
= 2; i
< input_len
; i
+= 2)
117 buffer
[buffer_count
++] = input
[i
] + (input
[i
+ 1] << 8);
120 *output_len
= WideCharToMultiByte(GetConsoleCP(), 0, buffer
, buffer_count
, (char *)output
, output_max
, NULL
, NULL
);
124 count_wchar
= MultiByteToWideChar(GetConsoleCP(), 0, (char *)input
, input_len
, buffer
, ARRAY_SIZE(buffer
));
125 *output_len
= WideCharToMultiByte(GetConsoleCP(), 0, buffer
, count_wchar
, (char *)output
, output_max
, NULL
, NULL
);
129 static void run_find_stdin_(const WCHAR
*commandline
, const BYTE
*input
, int input_len
, const BYTE
*out_expected
, int out_expected_len
, int exitcode_expected
, const char *file
, int line
)
131 HANDLE child_stdin_read
;
132 HANDLE child_stdout_write
;
133 HANDLE parent_stdin_write
;
134 HANDLE parent_stdout_read
;
135 STARTUPINFOW startup_info
= {0};
136 SECURITY_ATTRIBUTES security_attributes
;
137 PROCESS_INFORMATION process_info
= {0};
138 BYTE
*child_output
= NULL
;
139 int child_output_len
;
143 security_attributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
144 security_attributes
.bInheritHandle
= TRUE
;
145 security_attributes
.lpSecurityDescriptor
= NULL
;
147 CreatePipe(&parent_stdout_read
, &child_stdout_write
, &security_attributes
, 0);
148 CreatePipe(&child_stdin_read
, &parent_stdin_write
, &security_attributes
, 0);
150 SetHandleInformation(parent_stdout_read
, HANDLE_FLAG_INHERIT
, 0);
151 SetHandleInformation(parent_stdin_write
, HANDLE_FLAG_INHERIT
, 0);
153 startup_info
.cb
= sizeof(STARTUPINFOW
);
154 startup_info
.hStdInput
= child_stdin_read
;
155 startup_info
.hStdOutput
= child_stdout_write
;
156 startup_info
.hStdError
= NULL
;
157 startup_info
.dwFlags
|= STARTF_USESTDHANDLES
;
159 wsprintfW(cmd
, L
"find.exe %s", commandline
);
161 CreateProcessW(NULL
, cmd
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &startup_info
, &process_info
);
162 CloseHandle(child_stdin_read
);
163 CloseHandle(child_stdout_write
);
165 write_to_handle(parent_stdin_write
, input
, input_len
);
166 CloseHandle(parent_stdin_write
);
168 read_all_from_handle(parent_stdout_read
, &child_output
, &child_output_len
);
169 CloseHandle(parent_stdout_read
);
171 GetExitCodeProcess(process_info
.hProcess
, &exitcode
);
172 CloseHandle(process_info
.hProcess
);
173 CloseHandle(process_info
.hThread
);
175 check_find_output(child_output
, child_output_len
, out_expected
, out_expected_len
, file
, line
);
177 ok_(file
, line
)(exitcode
== exitcode_expected
, "Expected exitcode %d, got %d\n", exitcode_expected
, exitcode
);
179 heap_free(child_output
);
182 static void run_find_file_(const WCHAR
*commandline
, const BYTE
*input
, int input_len
, const BYTE
*out_expected
, int out_expected_len
, int exitcode_expected
, const char *file
, int line
)
184 char path_temp_file
[MAX_PATH
];
185 char path_temp_dir
[MAX_PATH
];
187 WCHAR commandline_new
[MAX_PATH
];
188 BYTE
*out_expected_new
;
189 char header
[MAX_PATH
];
192 GetTempPathA(ARRAY_SIZE(path_temp_dir
), path_temp_dir
);
193 GetTempFileNameA(path_temp_dir
, "", 0, path_temp_file
);
194 handle_file
= CreateFileA(path_temp_file
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
195 write_to_handle(handle_file
, input
, input_len
);
196 CloseHandle(handle_file
);
198 wsprintfW(commandline_new
, L
"%s %hs", commandline
, path_temp_file
);
200 CharUpperA(path_temp_file
);
201 wsprintfA(header
, "\r\n---------- %s\r\n", path_temp_file
);
202 header_len
= lstrlenA(header
);
203 out_expected_new
= heap_alloc(header_len
+ out_expected_len
);
204 memcpy(out_expected_new
, header
, header_len
);
205 memcpy(out_expected_new
+ header_len
, out_expected
, out_expected_len
);
207 run_find_stdin_(commandline_new
, (BYTE
*)"", 0, out_expected_new
, header_len
+ out_expected_len
, exitcode_expected
, file
, line
);
208 heap_free(out_expected_new
);
210 DeleteFileA(path_temp_file
);
213 #define run_find_stdin_str(commandline, input, out_expected, exitcode_expected) \
214 run_find_str_(commandline, input, out_expected, exitcode_expected, 0, __FILE__, __LINE__)
216 #define run_find_file_str(commandline, input, out_expected, exitcode_expected) \
217 run_find_str_(commandline, input, out_expected, exitcode_expected, 1, __FILE__, __LINE__)
219 static void run_find_str_(const char *commandline
, const char *input
, const char *out_expected
, int exitcode_expected
, BOOL is_file
, const char *file
, int line
)
222 int len_commandlineW
;
224 /* Turn commandline into WCHAR string */
225 len_commandlineW
= MultiByteToWideChar(CP_UTF8
, 0, commandline
, -1, 0, 0);
226 commandlineW
= heap_alloc(len_commandlineW
* sizeof(WCHAR
));
227 MultiByteToWideChar(CP_UTF8
, 0, commandline
, -1, commandlineW
, len_commandlineW
);
230 run_find_file_(commandlineW
, (BYTE
*)input
, lstrlenA(input
), (BYTE
*)out_expected
, lstrlenA(out_expected
), exitcode_expected
, file
, line
);
232 run_find_stdin_(commandlineW
, (BYTE
*)input
, lstrlenA(input
), (BYTE
*)out_expected
, lstrlenA(out_expected
), exitcode_expected
, file
, line
);
233 heap_free(commandlineW
);
236 #define run_find_stdin_unicode(input, out_expected, exitcode_expected) \
237 run_find_unicode_(input, sizeof(input), out_expected, sizeof(out_expected), exitcode_expected, 0, __FILE__, __LINE__)
239 #define run_find_file_unicode(input, out_expected, exitcode_expected) \
240 run_find_unicode_(input, sizeof(input), out_expected, sizeof(out_expected), exitcode_expected, 1, __FILE__, __LINE__)
242 static void run_find_unicode_(const BYTE
*input
, int input_len
, const BYTE
*out_expected
, int out_expected_len
, int exitcode_expected
, BOOL is_file
, const char *file
, int line
)
244 /* Need "test" as char and quoted wchar */
245 static const WCHAR wstr_quoted_test
[] = L
"\"test\"";
246 static const char str_test
[] = "test";
248 BYTE out_expected_mangled
[200] = {0};
249 int out_expected_mangled_len
;
251 mangle_text(out_expected
, out_expected_len
, out_expected_mangled
, ARRAY_SIZE(out_expected_mangled
), &out_expected_mangled_len
);
253 /* Mangling can destroy the test string, so check manually if it matches */
254 if (!strstr((char*)out_expected_mangled
, str_test
))
256 out_expected_mangled_len
= 0;
257 exitcode_expected
= 1;
261 run_find_file_(wstr_quoted_test
, input
, input_len
, out_expected_mangled
, out_expected_mangled_len
, exitcode_expected
, file
, line
);
263 run_find_stdin_(wstr_quoted_test
, input
, input_len
, out_expected_mangled
, out_expected_mangled_len
, exitcode_expected
, file
, line
);
266 static void run_find_file_multi(void)
268 char path_temp_file1
[MAX_PATH
];
269 char path_temp_file2
[MAX_PATH
];
270 char path_temp_file3
[MAX_PATH
];
271 char path_temp_dir
[MAX_PATH
];
273 WCHAR commandline_new
[MAX_PATH
];
274 char out_expected
[500];
275 const char* input
= "ab\nbd";
277 GetTempPathA(ARRAY_SIZE(path_temp_dir
), path_temp_dir
);
278 GetTempFileNameA(path_temp_dir
, "", 0, path_temp_file1
);
279 GetTempFileNameA(path_temp_dir
, "", 0, path_temp_file2
);
280 GetTempFileNameA(path_temp_dir
, "", 0, path_temp_file3
);
281 handle_file
= CreateFileA(path_temp_file1
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
282 write_to_handle(handle_file
, (BYTE
*)input
, strlen(input
));
283 CloseHandle(handle_file
);
284 handle_file
= CreateFileA(path_temp_file2
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
285 write_to_handle(handle_file
, (BYTE
*)input
, strlen(input
));
286 CloseHandle(handle_file
);
288 wsprintfW(commandline_new
, L
"\"b\" C:\\doesnotexist1 %hs C:\\doesnotexist1 %hs C:\\doesnotexist1 %hs", path_temp_file1
, path_temp_file2
, path_temp_file3
);
290 /* Keep file open during the test */
291 handle_file
= CreateFileA(path_temp_file3
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
293 CharUpperA(path_temp_file1
);
294 CharUpperA(path_temp_file2
);
295 CharUpperA(path_temp_file3
);
296 wsprintfA(out_expected
,
297 "File not found - C:\\DOESNOTEXIST1\r\n"
298 "\r\n---------- %s\r\n"
300 "File not found - C:\\DOESNOTEXIST1\r\n"
301 "\r\n---------- %s\r\n"
303 "File not found - C:\\DOESNOTEXIST1\r\n"
304 "File not found - %s\r\n",
305 path_temp_file1
, path_temp_file2
, path_temp_file3
);
307 run_find_stdin_(commandline_new
, (BYTE
*)"", 0, (BYTE
*)out_expected
, strlen(out_expected
), 0, __FILE__
, __LINE__
);
309 CloseHandle(handle_file
);
310 DeleteFileA(path_temp_file1
);
311 DeleteFileA(path_temp_file2
);
312 DeleteFileA(path_temp_file3
);
315 static void test_errors(void)
317 run_find_stdin_str("", "", "FIND: Parameter format not correct\r\n", 2);
318 todo_wine
/* Quotes are not properly passed into wine yet */
319 run_find_stdin_str("test", "", "FIND: Parameter format not correct\r\n", 2);
320 todo_wine
/* Quotes are not properly passed into wine yet */
321 run_find_stdin_str("\"test", "", "FIND: Parameter format not correct\r\n", 2);
322 run_find_stdin_str("\"test\" /XYZ", "", "FIND: Invalid switch\r\n", 2);
323 run_find_stdin_str("\"test\" C:\\doesnotexist.dat", "", "File not found - C:\\DOESNOTEXIST.DAT\r\n", 1);
326 static void test_singleline_without_switches(void)
328 run_find_stdin_str("\"\"", "test", "", 1);
329 run_find_stdin_str("\"test\"", "", "", 1);
330 run_find_stdin_str("\"test\"", "test", "test\r\n", 0);
331 run_find_stdin_str("\"test\"", "test2", "test2\r\n", 0);
332 run_find_stdin_str("\"test\"", "test\r2", "test\r2\r\n", 0);
333 run_find_stdin_str("\"test2\"", "test", "", 1);
336 static void test_multiline(void)
338 /* Newline in input shouldn't work */
339 run_find_stdin_str("\"t1\r\nt1\"", "t1\r\nt1", "", 1);
340 run_find_stdin_str("\"t1\nt1\"", "t1\nt1", "", 1);
342 /* Newline should always be displayed as \r\n */
343 run_find_stdin_str("\"test1\"", "test1\ntest2", "test1\r\n", 0);
344 run_find_stdin_str("\"test1\"", "test1\r\ntest2", "test1\r\n", 0);
346 /* Test with empty line */
347 run_find_stdin_str("\"test1\"", "test1\n\ntest2", "test1\r\n", 0);
349 /* Two strings to be found */
350 run_find_stdin_str("\"test\"", "junk1\ntest1\ntest2\r\njunk", "test1\r\ntest2\r\n", 0);
353 static const BYTE str_empty
[] = {};
354 static const BYTE str_jap_shiftjis
[] = { 0x8E,0x84,0x82,0xCD,'t','e','s','t','!','\r','\n' };
355 static const BYTE str_jap_utf8_bom
[] = { 0xEF,0xBB,0xBF,0xE7,0xA7,0x81,0xE3,0x81,0xAF,'j','a','p','t','e','s','t','!','\r','\n' };
356 static const BYTE str_jap_utf8_nobom
[] = { 0xE7,0xA7,0x81,0xE3,0x81,0xAF,'j','a','p','t','e','s','t','!','\r','\n' };
357 static const BYTE str_jap_utf16le_bom
[] = { 0xFF,0xFE,0xC1,0x79,0x6F,0x30,'t',0,'e',0,'s',0,'t',0,'!',0,'\r',0,'\n',0 };
358 static const BYTE str_jap_utf16le_nobom
[] = { 0xC1,0x79,0x6F,0x30,'t',0,'e',0,'s',0,'t',0,'!',0 };
359 static const BYTE str_jap_utf16be_bom
[] = { 0xFE,0xFF,0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' };
360 static const BYTE str_jap_utf16be_nobom
[] = { 0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' };
361 static const BYTE str_rus_utf8_bom
[] = { 0xEF,0xBB,0xBF,0xD0,0xBF,0xD1,0x80,0xD0,0xB8,0xD0,0xB2,0xD0,0xB5,0xD1,0x82,0x20,'t','e','s','t','!','\r','\n' };
362 static const BYTE str_rus_utf8_nobom
[] = { 0xD0,0xBF,0xD1,0x80,0xD0,0xB8,0xD0,0xB2,0xD0,0xB5,0xD1,0x82,0x20,'t','e','s','t','!','\r','\n' };
363 static const BYTE str_en_utf8_bom
[] = { 0xEF,0xBB,0xBF,'e','n','t','e','s','t','\r','\n' };
364 static const BYTE str_en_utf8_nobom
[] = { 'e','n','t','e','s','t','\r','\n' };
366 static void test_unicode_support_stdin(void)
368 /* Test unicode support on STDIN
369 * Those depend on the active codepage - e.g. 932 (japanese) behaves different from 1252 (latin)
370 * All unicode tests must check for the string "test".
374 run_find_stdin_unicode(str_en_utf8_nobom
, str_en_utf8_nobom
, 0);
375 run_find_stdin_unicode(str_en_utf8_bom
, str_en_utf8_bom
, 0);
377 /* Test russian characters */
378 run_find_stdin_unicode(str_rus_utf8_bom
, str_rus_utf8_bom
, 0);
379 run_find_stdin_unicode(str_rus_utf8_nobom
, str_rus_utf8_nobom
, 0);
381 /* Test japanese characters */
382 run_find_stdin_unicode(str_jap_utf8_nobom
, str_jap_utf8_nobom
, 0);
383 run_find_stdin_unicode(str_jap_utf8_bom
, str_jap_utf8_bom
, 0);
384 run_find_stdin_unicode(str_jap_shiftjis
, str_jap_shiftjis
, 0);
386 /* Test unsupported encodings */
387 run_find_stdin_unicode(str_jap_utf16le_nobom
, str_empty
, 1);
388 run_find_stdin_unicode(str_jap_utf16be_bom
, str_empty
, 1);
389 run_find_stdin_unicode(str_jap_utf16be_nobom
, str_empty
, 1);
393 run_find_stdin_unicode(str_jap_utf16le_bom
, str_jap_utf16le_bom
, 0);
396 static void test_file_search(void)
398 run_find_file_str("\"\"", "test", "", 1);
399 run_find_file_str("\"test\"", "", "", 1);
400 run_find_file_str("\"test\"", "test", "test\r\n", 0);
401 run_find_file_str("\"test\"", "test2", "test2\r\n", 0);
402 run_find_file_str("\"test\"", "test\r2", "test\r2\r\n", 0);
403 run_find_file_str("\"test2\"", "test", "", 1);
404 run_find_file_str("\"test\"", "test\nother\ntest2\ntest3", "test\r\ntest2\r\ntest3\r\n", 0);
407 static void test_unicode_support_file(void)
409 /* Test unicode support on files */
412 run_find_file_unicode(str_en_utf8_nobom
, str_en_utf8_nobom
, 0);
413 run_find_file_unicode(str_en_utf8_bom
, str_en_utf8_bom
, 0);
415 /* Test russian characters */
416 run_find_file_unicode(str_rus_utf8_bom
, str_rus_utf8_bom
, 0);
417 run_find_file_unicode(str_rus_utf8_nobom
, str_rus_utf8_nobom
, 0);
419 /* Test japanese characters */
420 run_find_file_unicode(str_jap_utf8_nobom
, str_jap_utf8_nobom
, 0);
421 run_find_file_unicode(str_jap_utf8_bom
, str_jap_utf8_bom
, 0);
422 run_find_file_unicode(str_jap_shiftjis
, str_jap_shiftjis
, 0);
424 /* Test unsupported encodings */
425 run_find_file_unicode(str_jap_utf16le_nobom
, str_empty
, 1);
426 run_find_file_unicode(str_jap_utf16be_bom
, str_empty
, 1);
427 run_find_file_unicode(str_jap_utf16be_nobom
, str_empty
, 1);
431 run_find_file_unicode(str_jap_utf16le_bom
, str_jap_utf16le_bom
, 0);
437 if (PRIMARYLANGID(GetUserDefaultUILanguage()) != LANG_ENGLISH
)
439 skip("Error tests only work with english locale.\n");
444 run_find_file_multi();
446 test_singleline_without_switches();
448 test_unicode_support_stdin();
450 test_unicode_support_file();