Signed-off-by: Fabian Maurer dark.shadow4@web.de --- programs/find/tests/Makefile.in | 5 + programs/find/tests/find.c | 316 ++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 programs/find/tests/Makefile.in create mode 100644 programs/find/tests/find.c
diff --git a/programs/find/tests/Makefile.in b/programs/find/tests/Makefile.in new file mode 100644 index 0000000000..c5b359bd98 --- /dev/null +++ b/programs/find/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = find.exe +IMPORTS = user32 + +C_SRCS = \ + find.c diff --git a/programs/find/tests/find.c b/programs/find/tests/find.c new file mode 100644 index 0000000000..9a7e65403f --- /dev/null +++ b/programs/find/tests/find.c @@ -0,0 +1,316 @@ +/* + * Copyright 2019 Fabian Maurer + * + * 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 <windows.h> +#include <stdio.h> + +#include "wine/heap.h" +#include "wine/test.h" + +void read_all_from_handle(HANDLE handle, BYTE **str, int *len) +{ + char buffer[4096]; + DWORD bytes_read; + DWORD length = 0; + BYTE *ret = heap_alloc_zero(1); + + for (;;) + { + BOOL success = ReadFile(handle, buffer, sizeof(buffer), &bytes_read, NULL); + if (!success || !bytes_read) + break; + ret = heap_realloc(ret, length + bytes_read); + memcpy((char *)ret + length, buffer, bytes_read); + length += bytes_read; + } + + *str = ret; + *len = length; +} + +void write_to_handle(HANDLE handle, const BYTE *str, int len) +{ + DWORD dummy; + WriteFile(handle, str, len, &dummy, NULL); +} + +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) +{ + BOOL strings_are_equal; + char *child_output_copy; + char *out_expected_copy; + int i, pos; + + if (child_output_len != out_expected_len) + strings_are_equal = FALSE; + else + { + strings_are_equal = memcmp(child_output, out_expected, out_expected_len) == 0; + } + + /* Format strings for debug printing */ + child_output_copy = heap_alloc_zero(child_output_len * 4 + 1); + out_expected_copy = heap_alloc_zero(out_expected_len * 4 + 1); + + for (i = 0, pos = 0; i < child_output_len; i++) + { + if (child_output[i] && child_output[i] != '\r' && child_output[i] < 128) + child_output_copy[pos++] = child_output[i]; + else + { + sprintf(&child_output_copy[pos], "\x%02x", child_output[i]); + pos += 4; + } + } + + for (i = 0, pos = 0; i < out_expected_len; i++) + { + if (out_expected[i] && out_expected[i] != '\r' && out_expected[i] < 128) + out_expected_copy[pos++] = out_expected[i]; + else + { + sprintf(&out_expected_copy[pos], "\x%02x", out_expected[i]); + pos += 4; + } + + } + + todo_wine_if(out_expected_len != 0) + ok_(file, line)(strings_are_equal, "\n#################### Expected:\n" + "%s\n" + "#################### But got:\n" + "%s\n" + "####################\n", + out_expected_copy, child_output_copy); + + heap_free(child_output_copy); + heap_free(out_expected_copy); +} + +static void mangle_text(const BYTE *input, int input_len, BYTE *output, int output_max, int *output_len) { + WCHAR buffer[200]; + int count_wchar; + + /* Check for UTF-16 LE BOM */ + if (input[0] == 0xFF && input[1] == 0xFE) + { + int buffer_count = 0; + int i; + + /* Copy utf16le into a WCHAR array, stripping the BOM */ + for (i = 2; i < input_len; i += 2) + { + buffer[buffer_count++] = input[i] + (input[i + 1] << 8); + } + + *output_len = WideCharToMultiByte(GetConsoleCP(), 0, buffer, buffer_count, (char *)output, output_max, NULL, NULL); + } + else + { + count_wchar = MultiByteToWideChar(GetConsoleCP(), 0, (char *)input, input_len, buffer, ARRAY_SIZE(buffer)); + *output_len = WideCharToMultiByte(GetConsoleCP(), 0, buffer, count_wchar, (char *)output, output_max, NULL, NULL); + } +} + +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) +{ + static const WCHAR find_exe[] = { 'f','i','n','d','.','e','x','e',' ','%','s',0 }; + HANDLE child_stdin_read; + HANDLE child_stdout_write; + HANDLE parent_stdin_write; + HANDLE parent_stdout_read; + STARTUPINFOW startup_info = {0}; + SECURITY_ATTRIBUTES security_attributes; + PROCESS_INFORMATION process_info = {0}; + BYTE *child_output = NULL; + int child_output_len; + WCHAR cmd[4096]; + DWORD exitcode; + + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + security_attributes.lpSecurityDescriptor = NULL; + + CreatePipe(&parent_stdout_read, &child_stdout_write, &security_attributes, 0); + CreatePipe(&child_stdin_read, &parent_stdin_write, &security_attributes, 0); + + SetHandleInformation(parent_stdout_read, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(parent_stdin_write, HANDLE_FLAG_INHERIT, 0); + + startup_info.cb = sizeof(STARTUPINFOW); + startup_info.hStdInput = child_stdin_read; + startup_info.hStdOutput = child_stdout_write; + startup_info.hStdError = NULL; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + + wsprintfW(cmd, find_exe, commandline); + + CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + CloseHandle(child_stdin_read); + CloseHandle(child_stdout_write); + + write_to_handle(parent_stdin_write, input, input_len); + CloseHandle(parent_stdin_write); + + read_all_from_handle(parent_stdout_read, &child_output, &child_output_len); + CloseHandle(parent_stdout_read); + + GetExitCodeProcess(process_info.hProcess, &exitcode); + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + + check_find_output(child_output, child_output_len, out_expected, out_expected_len, file, line); + + todo_wine_if(exitcode_expected != 0) + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %d, got %d\n", exitcode_expected, exitcode); + + heap_free(child_output); +} + +#define run_find_stdin_str(commandline, input, out_expected, exitcode_expected) \ + run_find_stdin_str_(commandline, input, out_expected, exitcode_expected, __FILE__, __LINE__) + +static void run_find_stdin_str_(const char *commandline, const char *input, const char *out_expected, int exitcode_expected, const char *file, int line) +{ + WCHAR *commandlineW; + int len_commandlineW; + + /* Turn commandline into WCHAR string */ + len_commandlineW = MultiByteToWideChar(CP_UTF8, 0, commandline, -1, 0, 0); + commandlineW = heap_alloc(len_commandlineW * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, commandline, -1, commandlineW, len_commandlineW); + + run_find_stdin_(commandlineW, (BYTE *)input, lstrlenA(input), (BYTE *)out_expected, lstrlenA(out_expected), exitcode_expected, file, line); + + heap_free(commandlineW); +} + +#define run_find_stdin_unicode(input, out_expected, exitcode_expected) \ + run_find_stdin_unicode_(input, sizeof(input), out_expected, sizeof(out_expected), exitcode_expected, __FILE__, __LINE__) + +static void run_find_stdin_unicode_(const BYTE *input, int input_len, const BYTE *out_expected, int out_expected_len, int exitcode_expected, const char *file, int line) +{ + /* Need "test" as char and quoted wchar */ + static const WCHAR wstr_quoted_test[] = { '"','t', 'e', 's', 't','"',0 }; + static const char str_test[] = "test"; + + BYTE out_expected_mangled[200] = {0}; + int out_expected_mangled_len; + + mangle_text(out_expected, out_expected_len, out_expected_mangled, ARRAY_SIZE(out_expected_mangled), &out_expected_mangled_len); + + /* Mangling can destroy the test string, so check manually if it matches */ + if (!strstr((char*)out_expected_mangled, str_test)) + { + out_expected_mangled_len = 0; + exitcode_expected = 1; + } + + run_find_stdin_(wstr_quoted_test, input, input_len, out_expected_mangled, out_expected_mangled_len, exitcode_expected, file, line); +} + +static void test_errors(void) +{ + run_find_stdin_str("", "", "FIND: Parameter format not correct\r\n", 2); + run_find_stdin_str("test", "", "FIND: Parameter format not correct\r\n", 2); + run_find_stdin_str(""test", "", "FIND: Parameter format not correct\r\n", 2); + run_find_stdin_str(""test" /XYZ", "", "FIND: Invalid switch\r\n", 2); +} + +static void test_singleline_without_switches(void) +{ + run_find_stdin_str("""", "test", "", 1); + run_find_stdin_str(""test"", "", "", 1); + run_find_stdin_str(""test"", "test", "test\r\n", 0); + run_find_stdin_str(""test"", "test2", "test2\r\n", 0); + run_find_stdin_str(""test2"", "test", "", 1); +} + +static void test_multiline(void) +{ + /* Newline in input shouldn't work */ + run_find_stdin_str(""t1\r\nt1"", "t1\r\nt1", "", 1); + run_find_stdin_str(""t1\nt1"", "t1\nt1", "", 1); + + /* Newline should always be displayed as \r\n */ + run_find_stdin_str(""test1"", "test1\ntest2", "test1\r\n", 0); + run_find_stdin_str(""test1"", "test1\r\ntest2", "test1\r\n", 0); + + /* Test with empty line */ + run_find_stdin_str(""test1"", "test1\n\ntest2", "test1\r\n", 0); + + /* Two strings to be found */ + run_find_stdin_str(""test"", "junk1\ntest1\ntest2\r\njunk", "test1\r\ntest2\r\n", 0); +} + +static const BYTE str_empty[] = {}; +static const BYTE str_jap_shiftjis[] = { 0x8E,0x84,0x82,0xCD,'t','e','s','t','!','\r','\n' }; +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' }; +static const BYTE str_jap_utf8_nobom[] = { 0xE7,0xA7,0x81,0xE3,0x81,0xAF,'j','a','p','t','e','s','t','!','\r','\n' }; +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 }; +static const BYTE str_jap_utf16le_nobom[] = { 0xC1,0x79,0x6F,0x30,'t',0,'e',0,'s',0,'t',0,'!',0 }; +static const BYTE str_jap_utf16be_bom[] = { 0xFE,0xFF,0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' }; +static const BYTE str_jap_utf16be_nobom[] = { 0x79,0xC1,0x30,0x6F,0,'t',0,'e',0,'s',0,'t',0,'!' }; +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' }; +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' }; +static const BYTE str_en_utf8_bom[] = { 0xEF,0xBB,0xBF,'e','n','t','e','s','t','\r','\n' }; +static const BYTE str_en_utf8_nobom[] = { 'e','n','t','e','s','t','\r','\n' }; + +static void test_unicode_support(void) +{ + /* Test unicode support on STDIN + * Those depend on the active codepage - e.g. 932 (japanese) behaves different from 1252 (latin) + * All unicode tests must check for the string "test". + */ + + /* Test UTF-8 BOM */ + run_find_stdin_unicode(str_en_utf8_nobom, str_en_utf8_nobom, 0); + run_find_stdin_unicode(str_en_utf8_bom, str_en_utf8_bom, 0); + + /* Test russian characters */ + run_find_stdin_unicode(str_rus_utf8_bom, str_rus_utf8_bom, 0); + run_find_stdin_unicode(str_rus_utf8_nobom, str_rus_utf8_nobom, 0); + + /* Test japanese characters */ + run_find_stdin_unicode(str_jap_utf8_nobom, str_jap_utf8_nobom, 0); + run_find_stdin_unicode(str_jap_utf8_bom, str_jap_utf8_bom, 0); + run_find_stdin_unicode(str_jap_shiftjis, str_jap_shiftjis, 0); + + /* Test unsupported encodings */ + run_find_stdin_unicode(str_jap_utf16le_nobom, str_empty, 1); + run_find_stdin_unicode(str_jap_utf16be_bom, str_empty, 1); + run_find_stdin_unicode(str_jap_utf16be_nobom, str_empty, 1); + + /* Test utf16le */ + run_find_stdin_unicode(str_jap_utf16le_bom, str_jap_utf16le_bom, 0); +} + +START_TEST(find) +{ + if (PRIMARYLANGID(GetUserDefaultUILanguage()) != LANG_ENGLISH) + { + skip("Error tests only work with english locale.\n"); + } + else + { + test_errors(); + } + test_singleline_without_switches(); + test_multiline(); + test_unicode_support(); +} -- 2.22.0