On Tue, May 3, 2016 at 1:31 PM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: >> >> Frediano, >> >> On Tue, May 3, 2016 at 11:26 AM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: >> > This program attempt multiple redirection combination: >> > - passing handles using either CreateProcess or SetStdHandle; >> > - having a console or not; >> > - redirection stdout/stderr yes or not. >> >> Would worth to mention (even if it looks obvious) that the test needs >> to be executed in a native MingW (no cross compilation). >> >> If you agree, I'll add this to the commit log and push both patches. >> > > Agreed. > > Note that this apply to all tests in tests directory so it's not a new > issue. To execute binaries compiled with MingW you need either native > MingW or Wine (with .exe executables enabled in Linux binfmt). Modified the commit log as we discussed and pushed. Thanks for the patch! > > Frediano > >> > >> > Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> >> > --- >> > tests/Makefile.am | 7 ++ >> > tests/redirect-test.c | 335 >> > ++++++++++++++++++++++++++++++++++++++++++++++++++ >> > 2 files changed, 342 insertions(+) >> > create mode 100644 tests/redirect-test.c >> > >> > Changes since v3: >> > - moved source to tests directory; >> > - added missing config.h include; >> > - remove some unused warnings. >> > >> > diff --git a/tests/Makefile.am b/tests/Makefile.am >> > index 15907eb..a1bdf85 100644 >> > --- a/tests/Makefile.am >> > +++ b/tests/Makefile.am >> > @@ -26,4 +26,11 @@ test_monitor_mapping_SOURCES = \ >> > test-monitor-mapping.c \ >> > $(NULL) >> > >> > +if OS_WIN32 >> > +TESTS += redirect-test >> > +redirect_test_SOURCES = redirect-test.c >> > +redirect_test_LDFLAGS = -Wl,--subsystem,windows >> > +redirect_test_CPPFLAGS = $(GLIB2_CFLAGS) >> > +endif >> > + >> > -include $(top_srcdir)/git.mk >> > diff --git a/tests/redirect-test.c b/tests/redirect-test.c >> > new file mode 100644 >> > index 0000000..be96cd0 >> > --- /dev/null >> > +++ b/tests/redirect-test.c >> > @@ -0,0 +1,335 @@ >> > +/* >> > + * Copyright (C) 2016 Red Hat, Inc. >> > + * >> > + * This program is free software; you can redistribute it and/or modify >> > + * it under the terms of the GNU General Public License as published by >> > + * the Free Software Foundation; either version 2 of the License, or >> > + * (at your option) any later version. >> > + * >> > + * This program 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 General Public License for more details. >> > + * >> > + * You should have received a copy of the GNU General Public License >> > + * along with this program; if not, write to the Free Software >> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >> > USA >> > + */ >> > + >> > +/* This small program test redirection inside Remote Viewer >> > + */ >> > + >> > +#include <config.h> >> > + >> > +/* force assert to be compiler, on Windows are usually disabled */ >> > +#undef NDEBUG >> > +#undef DEBUG >> > +#define DEBUG 1 >> > + >> > +#include <windows.h> >> > +#include <stdio.h> >> > +#include <stdlib.h> >> > +#include <assert.h> >> > +#include <io.h> >> > +#include <glib.h> >> > + >> > +static BOOL has_console = FALSE; >> > +static HANDLE h_console = INVALID_HANDLE_VALUE; >> > +static FILE *log_f = NULL; >> > +static int num_test = 0; >> > + >> > +static int >> > +has_inherit(HANDLE h) >> > +{ >> > + DWORD flags; >> > + if (GetHandleInformation(h, &flags)) >> > + return flags & HANDLE_FLAG_INHERIT; >> > + return 0; >> > +} >> > + >> > +static BOOL is_handle_valid(HANDLE h) >> > +{ >> > + if (h == INVALID_HANDLE_VALUE || h == NULL) >> > + return FALSE; >> > + >> > + DWORD flags; >> > + return GetHandleInformation(h, &flags); >> > +} >> > + >> > +static int >> > +called_test(int argc G_GNUC_UNUSED, char **argv) >> > +{ >> > + STARTUPINFO si; >> > + memset(&si, 0, sizeof(si)); >> > + si.cb = sizeof(si); >> > + si.dwFlags = STARTF_USESTDHANDLES; >> > + GetStartupInfo(&si); >> > + fprintf(log_f, "handles %p %p - %p %p %d %d\n", si.hStdOutput, >> > si.hStdError, >> > + GetStdHandle(STD_OUTPUT_HANDLE), >> > GetStdHandle(STD_ERROR_HANDLE), >> > + has_inherit(GetStdHandle(STD_OUTPUT_HANDLE)), >> > has_inherit(GetStdHandle(STD_ERROR_HANDLE))); >> > + >> > + /* Get redirection from parent */ >> > + BOOL out_valid = is_handle_valid(GetStdHandle(STD_OUTPUT_HANDLE)); >> > + BOOL err_valid = is_handle_valid(GetStdHandle(STD_ERROR_HANDLE)); >> > + >> > + /* >> > + * If not all output are redirected try to redirect to parent console. >> > + * If parent has no console (for instance as launched from GUI) just >> > + * rely on default (no output). >> > + */ >> > + if ((!out_valid || !err_valid) && >> > AttachConsole(ATTACH_PARENT_PROCESS)) { >> > + fprintf(log_f, "attached\n"); >> > + if (!out_valid) { >> > + freopen("CONOUT$", "w", stdout); >> > + dup2(fileno(stdout), STDOUT_FILENO); >> > + } >> > + if (!err_valid) { >> > + freopen("CONOUT$", "w", stderr); >> > + dup2(fileno(stderr), STDERR_FILENO); >> > + } >> > + } >> > + >> > + printf("stdout %s line\n", argv[0]); >> > + fflush(stdout); >> > + fprintf(stderr, "stderr %s line\n", argv[0]); >> > + fflush(stderr); >> > + return 0; >> > +} >> > + >> > +static enum { >> > + METHOD_CREATEPROCESS, >> > + METHOD_STDHANDLE >> > +} redir_method = METHOD_CREATEPROCESS; >> > + >> > +static void >> > +exec(HANDLE redir_out, HANDLE redir_err) >> > +{ >> > + char program[MAX_PATH+128]; >> > + GetModuleFileName(NULL, program+1, MAX_PATH); >> > + program[0] = '\"'; >> > + sprintf(strchr(program, 0) , "\" %d", num_test); >> > + >> > + HANDLE old_out = GetStdHandle(STD_OUTPUT_HANDLE); >> > + HANDLE old_err = GetStdHandle(STD_ERROR_HANDLE); >> > + >> > + BOOL inherit = FALSE; >> > + STARTUPINFO si; >> > + memset(&si, 0, sizeof(si)); >> > + si.cb = sizeof(si); >> > + si.dwFlags = 0; >> > + si.hStdOutput = si.hStdError = si.hStdInput = INVALID_HANDLE_VALUE; >> > + if (redir_out != INVALID_HANDLE_VALUE || redir_err != >> > INVALID_HANDLE_VALUE) { >> > + if (redir_method == METHOD_CREATEPROCESS) >> > + si.dwFlags |= STARTF_USESTDHANDLES; >> > + inherit = TRUE; >> > + if (redir_out != INVALID_HANDLE_VALUE) { >> > + SetHandleInformation(redir_out, HANDLE_FLAG_INHERIT, >> > HANDLE_FLAG_INHERIT); >> > + if (redir_method == METHOD_CREATEPROCESS) { >> > + si.hStdOutput = redir_out; >> > + } else { >> > + SetStdHandle(STD_OUTPUT_HANDLE, redir_out); >> > + } >> > + } >> > + if (redir_err != INVALID_HANDLE_VALUE) { >> > + SetHandleInformation(redir_err, HANDLE_FLAG_INHERIT, >> > HANDLE_FLAG_INHERIT); >> > + if (redir_method == METHOD_CREATEPROCESS) { >> > + si.hStdError = redir_err; >> > + } else { >> > + SetStdHandle(STD_ERROR_HANDLE, redir_err); >> > + } >> > + } >> > + } >> > + >> > + fprintf(log_f, "handles %p %p - %p %p %d %d\n", si.hStdOutput, >> > si.hStdError, >> > + GetStdHandle(STD_OUTPUT_HANDLE), >> > GetStdHandle(STD_ERROR_HANDLE), >> > + has_inherit(GetStdHandle(STD_OUTPUT_HANDLE)), >> > has_inherit(GetStdHandle(STD_ERROR_HANDLE))); >> > + >> > + fprintf(log_f, "sub command ---->\n"); >> > + fclose(log_f); >> > + log_f = NULL; >> > + >> > + PROCESS_INFORMATION pi = { 0, }; >> > + assert(CreateProcess(NULL, program, NULL, NULL, inherit, 0, NULL, >> > NULL, &si, &pi)); >> > + CloseHandle(pi.hThread); >> > + WaitForSingleObject(pi.hProcess, INFINITE); >> > + CloseHandle(pi.hProcess); >> > + >> > + log_f = fopen("log.txt", "a"); >> > + assert(log_f); >> > + setbuf(log_f, NULL); >> > + fprintf(log_f, "<---- sub command\n"); >> > + >> > + SetStdHandle(STD_OUTPUT_HANDLE, old_out); >> > + SetStdHandle(STD_ERROR_HANDLE, old_err); >> > +} >> > + >> > +// simple dirty function to read first line in a file >> > +static char * >> > +read_file(const char *fn) >> > +{ >> > + FILE *f = fopen(fn, "r"); >> > + assert(f); >> > + >> > + // dirty but fast >> > + static char buf[1024]; >> > + >> > + memset(buf, 0, sizeof(buf)); >> > + if (!fgets(buf, sizeof(buf), f)) >> > + memset(buf, 0, sizeof(buf)); >> > + >> > + fclose(f); >> > + return buf; >> > +} >> > + >> > +static char * >> > +read_console(void) >> > +{ >> > + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; >> > + assert(GetConsoleScreenBufferInfo(h_console, &csbiInfo)); >> > + >> > + fprintf(log_f, "size %d %d\n", csbiInfo.dwSize.X, csbiInfo.dwSize.Y); >> > + fprintf(log_f, "win %d %d %d %d\n", csbiInfo.srWindow.Left, >> > csbiInfo.srWindow.Top, csbiInfo.srWindow.Right, csbiInfo.srWindow.Bottom); >> > + >> > + COORD size; >> > + size.Y = csbiInfo.srWindow.Bottom + 1; >> > + size.X = csbiInfo.srWindow.Right + 1; >> > + >> > + COORD coord = { 0, 0 }; >> > + >> > + CHAR_INFO *buf = calloc(size.Y * size.X, sizeof(CHAR_INFO)); >> > + assert(buf); >> > + >> > + SMALL_RECT rect; >> > + rect.Top = 0; >> > + rect.Left = 0; >> > + rect.Bottom = size.Y - 1; >> > + rect.Right = size.X - 1; >> > + >> > + // read console content >> > + assert(ReadConsoleOutput(h_console, buf, size, coord, &rect)); >> > + >> > + // convert to just text >> > + unsigned n; >> > + char *char_buf = (char *) buf; >> > + for (n = 0; n < size.X * size.Y; ++n) >> > + char_buf[n] = buf[n].Char.AsciiChar; >> > + char_buf[n] = 0; >> > + >> > + // remove additional spaces >> > + char *p; >> > + while ((p=strstr(char_buf, " ")) != NULL) >> > + memmove(p, p+1, strlen(p)); >> > + >> > + return char_buf; >> > +} >> > + >> > +static void >> > +check(BOOL redir_out, BOOL redir_err) >> > +{ >> > + ++num_test; >> > + fprintf(log_f, "check method %d console %d redir_out %d redir_err >> > %d\n", >> > + (int) redir_method, has_console, redir_out, redir_err); >> > + >> > + char stdout_line[64], stderr_line[64]; >> > + >> > + sprintf(stdout_line, "stdout %d line", num_test); >> > + sprintf(stderr_line, "stderr %d line", num_test); >> > + >> > + DeleteFile("stdout.txt"); >> > + DeleteFile("stderr.txt"); >> > + >> > + HANDLE out = CreateFile("stdout.txt", GENERIC_WRITE, 0, NULL, >> > CREATE_ALWAYS, 0, NULL); >> > + assert(out != INVALID_HANDLE_VALUE); >> > + HANDLE err = CreateFile("stderr.txt", GENERIC_WRITE, 0, NULL, >> > CREATE_ALWAYS, 0, NULL); >> > + assert(err != INVALID_HANDLE_VALUE); >> > + >> > + exec(redir_out ? out : INVALID_HANDLE_VALUE, >> > + redir_err ? err : INVALID_HANDLE_VALUE); >> > + >> > + CloseHandle(out); >> > + CloseHandle(err); >> > + >> > + // check file output >> > + if (redir_out) { >> > + char *data = read_file("stdout.txt"); >> > + assert(strstr(data, stdout_line) != NULL); >> > + } >> > + if (redir_err) { >> > + char *data = read_file("stderr.txt"); >> > + assert(strstr(data, stderr_line) != NULL); >> > + } >> > + >> > + DeleteFile("stdout.txt"); >> > + DeleteFile("stderr.txt"); >> > + >> > + // check console output >> > + if (!has_console) >> > + return; >> > + >> > + char *data = read_console(); >> > + fprintf(log_f, "\nconsole: %s\nstdout expected: %s\nstderr expected: >> > %s\n", data, stdout_line, stderr_line); >> > + if (!redir_out) >> > + assert(strstr(data, stdout_line) != NULL); >> > + >> > + if (!redir_err) >> > + assert(strstr(data, stderr_line) != NULL); >> > + free(data); >> > +} >> > + >> > +static void >> > +all_checks(void) >> > +{ >> > + check(TRUE, FALSE); >> > + check(FALSE, TRUE); >> > + check(TRUE, TRUE); >> > + >> > + assert(AllocConsole()); >> > + has_console = TRUE; >> > + h_console = GetStdHandle(STD_OUTPUT_HANDLE); >> > + fprintf(log_f, "got console handles %p %p\n", h_console, >> > GetStdHandle(STD_ERROR_HANDLE)); >> > + >> > + check(FALSE, FALSE); >> > + check(TRUE, FALSE); >> > + check(FALSE, TRUE); >> > + check(TRUE, TRUE); >> > + >> > + assert(FreeConsole()); >> > + has_console = FALSE; >> > + h_console = INVALID_HANDLE_VALUE; >> > +} >> > + >> > +int WINAPI WinMain(HINSTANCE hInstance G_GNUC_UNUSED, HINSTANCE >> > hPrevInstance G_GNUC_UNUSED, >> > + LPSTR lpCmdLine, int nShowCmd G_GNUC_UNUSED) >> > +{ >> > + static const char cmd_seps[] = " \t\r\n\v"; >> > + >> > + char *argv[10], *p; >> > + int argc = 0; >> > + >> > + // parse arguments >> > + for (p = strtok(lpCmdLine, cmd_seps); p && argc < 10; p = strtok(NULL, >> > cmd_seps)) >> > + argv[argc++] = p; >> > + argv[argc] = NULL; >> > + >> > + log_f = fopen("log.txt", argc >= 1 ? "a" : "w"); >> > + assert(log_f); >> > + setbuf(log_f, NULL); >> > + >> > + fprintf(log_f, "argc %d argv[0] %s \n", argc, argv[0]); >> > + >> > + if (argc >= 1) { >> > + return called_test(argc, argv); >> > + } >> > + >> > + // main program, call ourself with different arguments and settings >> > + redir_method = METHOD_CREATEPROCESS; >> > + all_checks(); >> > + >> > + redir_method = METHOD_STDHANDLE; >> > + all_checks(); >> > + >> > + fprintf(log_f, "everything ok\n"); >> > + >> > + fclose(log_f); >> > + return 0; >> > +} >> > -- >> > 2.5.5 >> > >> > _______________________________________________ >> > Spice-devel mailing list >> > Spice-devel@xxxxxxxxxxxxxxxxxxxxx >> > https://lists.freedesktop.org/mailman/listinfo/spice-devel >> >> >> >> -- >> Fabiano Fidêncio >> -- Fabiano Fidêncio _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel