X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=test.c;h=8c8a80ec68600a432480d11711418f42f77550f2;hp=9694161b81567c7509c393b4426bdb742f414c7d;hb=cbb91c20268bed939c91654c3935bd33055118e7;hpb=60807b677cecb414b501503d73b50714534ff3b0 diff --git a/test.c b/test.c index 9694161..8c8a80e 100644 --- a/test.c +++ b/test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 + * Copyright (C) 2012, 2013 * Dale Weiler * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -23,9 +23,8 @@ #include "gmqcc.h" #include #include -#include -cmd_options opts; +opts_cmd_t opts; char *task_bins[] = { "./gmqcc", @@ -50,7 +49,7 @@ char *task_bins[] = { #ifndef _WIN32 #include #include - +#include #include typedef struct { FILE *handles[3]; @@ -122,16 +121,12 @@ FILE ** task_popen(const char *command, const char *mode) { close(2), dup(errhandle[1]); execvp(*argv, argv); - exit(1); + exit(EXIT_FAILURE); } else { /* fork failed */ goto task_popen_error_3; } - if (argv) - vec_free(argv); - return data->handles; - task_popen_error_3: close(errhandle[0]), close(errhandle[1]); task_popen_error_2: close(outhandle[0]), close(outhandle[1]); task_popen_error_1: close(inhandle [0]), close(inhandle [1]); @@ -157,7 +152,120 @@ int task_pclose(FILE **handles) { return status; } #else +# define _WIN32_LEAN_AND_MEAN +# define popen _popen +# define pclose _pclose +# include +# include +# include + /* + * Bidirectional piping implementation for windows using CreatePipe and DuplicateHandle + + * other hacks. + */ + + typedef struct { + int __dummy; + /* TODO: implement */ + } popen_t; + + FILE **task_popen(const char *command, const char *mode) { + (void)command; + (void)mode; + + /* TODO: implement */ + return NULL; + } + + void task_pclose(FILE **files) { + /* TODO: implement */ + (void)files; + return; + } + +# ifdef __MINGW32__ + /* mingw32 has dirent.h */ +# include +# elif defined (_WIN32) + /* + * visual studio lacks dirent.h it's a posix thing + * so we emulate it with the WinAPI. + */ + struct dirent { + long d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[FILENAME_MAX]; + }; + + typedef struct { + struct _finddata_t dd_dta; + struct dirent dd_dir; + long dd_handle; + int dd_stat; + char dd_name[1]; + } DIR; + + DIR *opendir(const char *name) { + DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name)); + if (!dir) + return NULL; + + strcpy(dir->dd_name, name); + return dir; + } + + int closedir(DIR *dir) { + FindClose((HANDLE)dir->dd_handle); + mem_d ((void*)dir); + return 0; + } + + struct dirent *readdir(DIR *dir) { + WIN32_FIND_DATA info; + struct dirent *data; + int rets; + + if (!dir->dd_handle) { + char *dirname; + if (*dir->dd_name) { + size_t n = strlen(dir->dd_name); + if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) { + strcpy(dirname, dir->dd_name); + strcpy(dirname + n, "\\*.*"); /* 4 + 1 */ + } + } else { + if (!(dirname = util_strdup("\\*.*"))) + return NULL; + } + + dir->dd_handle = (long)FindFirstFile(dirname, &info); + mem_d(dirname); + rets = !(!dir->dd_handle); + } else if (dir->dd_handle != -11) { + rets = FindNextFile ((HANDLE)dir->dd_handle, &info); + } else { + rets = 0; + } + + if (!rets) + return NULL; + + if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) { + strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1); + data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */ + data->d_namlen = strlen(data->d_name); + } + return data; + } + + /* + * Visual studio also lacks S_ISDIR for sys/stat.h, so we emulate this as well + * which is not hard at all. + */ +# undef S_ISDIR /* undef just incase */ +# define S_ISDIR(X) ((X)&_S_IFDIR) +# endif #endif #define TASK_COMPILE 0 @@ -201,6 +309,9 @@ int task_pclose(FILE **handles) { * This simply performs compilation only * -execute * This will perform compilation and execution + * -fail + * This will perform compilation, but requires + * the compilation to fail in order to succeed. * * This must be provided, this tag is NOT optional. * @@ -299,7 +410,10 @@ bool task_template_generate(task_template_t *template, char tag, const char *fil * Value will contain a newline character at the end, we need to strip * this otherwise kaboom, seriously, kaboom :P */ - *strrchr(value, '\n')='\0'; + if (strchr(value, '\n')) + *strrchr(value, '\n')='\0'; + else /* cppcheck: possible nullpointer dereference */ + abort(); /* * Now allocate and set the actual value for the specific tag. Which @@ -320,7 +434,7 @@ bool task_template_parse(const char *file, task_template_t *template, FILE *fp) return false; /* top down parsing */ - while (util_getline(&back, &size, fp) != EOF) { + while (file_getline(&back, &size, fp) != EOF) { /* skip whitespace */ data = back; if (*data && (*data == ' ' || *data == '\t')) @@ -400,7 +514,10 @@ bool task_template_parse(const char *file, task_template_t *template, FILE *fp) * Value will contain a newline character at the end, we need to strip * this otherwise kaboom, seriously, kaboom :P */ - *strrchr(value, '\n')='\0'; + if (strrchr(value, '\n')) + *strrchr(value, '\n')='\0'; + else /* cppcheck: possible null pointer dereference */ + abort(); vec_push(template->comparematch, util_strdup(value)); @@ -458,7 +575,7 @@ task_template_t *task_template_compile(const char *file, const char *dir) { memset (fullfile, 0, sizeof(fullfile)); snprintf(fullfile, sizeof(fullfile), "%s/%s", dir, file); - tempfile = fopen(fullfile, "r"); + tempfile = file_open(fullfile, "r"); template = mem_a(sizeof(task_template_t)); task_template_nullify(template); @@ -521,13 +638,19 @@ task_template_t *task_template_compile(const char *file, const char *dir) { con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file); goto failure; } + } else if (!strcmp(template->proceduretype, "-fail")) { + if (template->executeflags) + con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file); + if (template->comparematch) + con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file); + goto success; } else { con_err("template compile error: %s invalid procedure type: %s\n", file, template->proceduretype); goto failure; } success: - fclose(tempfile); + file_close(tempfile); return template; failure: @@ -536,7 +659,7 @@ failure: * so the check to see if it's not null here is required. */ if (tempfile) - fclose(tempfile); + file_close(tempfile); mem_d (template); return NULL; @@ -623,6 +746,7 @@ bool task_propagate(const char *curdir) { if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) { task_template_t *template = task_template_compile(files->d_name, curdir); char buf[4096]; /* one page should be enough */ + char *qcflags = NULL; task_t task; util_debug("TEST", "compiling task template: %s/%s\n", curdir, files->d_name); @@ -638,19 +762,37 @@ bool task_propagate(const char *curdir) { */ template->tempfilename = tempnam(curdir, "TMPDAT"); + /* + * Additional QCFLAGS enviroment variable may be used + * to test compile flags for all tests. This needs to be + * BEFORE other flags (so that the .tmpl can override them) + */ + qcflags = getenv("QCFLAGS"); + /* * Generate the command required to open a pipe to a process * which will be refered to with a handle in the task for * reading the data from the pipe. */ - memset (buf,0,sizeof(buf)); - snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s", - task_bins[TASK_COMPILE], - curdir, - template->sourcefile, - template->compileflags, - template->tempfilename - ); + memset (buf,0,sizeof(buf)); + if (qcflags) { + snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + template->sourcefile, + qcflags, + template->compileflags, + template->tempfilename + ); + } else { + snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + template->sourcefile, + template->compileflags, + template->tempfilename + ); + } /* * The task template was compiled, now lets create a task from @@ -672,7 +814,7 @@ bool task_propagate(const char *curdir) { memset (buf,0,sizeof(buf)); snprintf(buf, sizeof(buf), "%s.stdout", template->tempfilename); task.stdoutlogfile = util_strdup(buf); - if (!(task.stdoutlog = fopen(buf, "w"))) { + if (!(task.stdoutlog = file_open(buf, "w"))) { con_err("error opening %s for stdout\n", buf); continue; } @@ -680,7 +822,7 @@ bool task_propagate(const char *curdir) { memset (buf,0,sizeof(buf)); snprintf(buf, sizeof(buf), "%s.stderr", template->tempfilename); task.stderrlogfile = util_strdup(buf); - if (!(task.stderrlog = fopen(buf, "w"))) { + if (!(task.stderrlog = file_open(buf, "w"))) { con_err("error opening %s for stderr\n", buf); continue; } @@ -698,31 +840,6 @@ bool task_propagate(const char *curdir) { return success; } -/* - * Removes all temporary 'progs.dat' files created during compilation - * of all tests' - */ -void task_cleanup(const char *curdir) { - DIR *dir; - struct dirent *files; - char buffer[4096]; - - dir = opendir(curdir); - - while ((files = readdir(dir))) { - memset(buffer, 0, sizeof(buffer)); - if (strstr(files->d_name, "TMP")) { - snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name); - if (remove(buffer)) - con_err("error removing temporary file: %s\n", buffer); - else - util_debug("TEST", "removed temporary file: %s\n", buffer); - } - } - - closedir(dir); -} - /* * Task precleanup removes any existing temporary files or log files * left behind from a previous invoke of the test-suite. @@ -751,7 +868,7 @@ void task_precleanup(const char *curdir) { closedir(dir); } -void task_destroy(const char *curdir) { +void task_destroy(void) { /* * Free all the data in the task list and finally the list itself * then proceed to cleanup anything else outside the program like @@ -764,23 +881,25 @@ void task_destroy(const char *curdir) { * annoying to have to do all this cleanup work. */ if (task_tasks[i].runhandles) task_pclose(task_tasks[i].runhandles); - if (task_tasks[i].stdoutlog) fclose (task_tasks[i].stdoutlog); - if (task_tasks[i].stderrlog) fclose (task_tasks[i].stderrlog); + if (task_tasks[i].stdoutlog) file_close (task_tasks[i].stdoutlog); + if (task_tasks[i].stderrlog) file_close (task_tasks[i].stderrlog); /* * Only remove the log files if the test actually compiled otherwise - * forget about it. + * forget about it (or if it didn't compile, and the procedure type + * was set to -fail (meaning it shouldn't compile) .. stil remove) */ - if (task_tasks[i].compiled) { + if (task_tasks[i].compiled || !strcmp(task_tasks[i].template->proceduretype, "-fail")) { if (remove(task_tasks[i].stdoutlogfile)) con_err("error removing stdout log file: %s\n", task_tasks[i].stdoutlogfile); else util_debug("TEST", "removed stdout log file: %s\n", task_tasks[i].stdoutlogfile); - if (remove(task_tasks[i].stderrlogfile)) con_err("error removing stderr log file: %s\n", task_tasks[i].stderrlogfile); else util_debug("TEST", "removed stderr log file: %s\n", task_tasks[i].stderrlogfile); + + remove(task_tasks[i].template->tempfilename); } /* free util_strdup data for log files */ @@ -790,11 +909,6 @@ void task_destroy(const char *curdir) { task_template_destroy(&task_tasks[i].template); } vec_free(task_tasks); - - /* - * Cleanup outside stuff like temporary files. - */ - task_cleanup(curdir); } /* @@ -842,7 +956,7 @@ bool task_execute(task_template_t *template, char ***line) { char *data = NULL; size_t size = 0; size_t compare = 0; - while (util_getline(&data, &size, execute) != EOF) { + while (file_getline(&data, &size, execute) != EOF) { if (!strcmp(data, "No main function found\n")) { con_err("test failure: `%s` [%s] (No main function found)\n", template->description, @@ -918,8 +1032,8 @@ void task_schedualize() { * Read data from stdout first and pipe that stuff into a log file * then we do the same for stderr. */ - while (util_getline(&data, &size, task_tasks[i].runhandles[1]) != EOF) { - fputs(data, task_tasks[i].stdoutlog); + while (file_getline(&data, &size, task_tasks[i].runhandles[1]) != EOF) { + file_puts(task_tasks[i].stdoutlog, data); if (strstr(data, "failed to open file")) { task_tasks[i].compiled = false; @@ -928,7 +1042,7 @@ void task_schedualize() { fflush(task_tasks[i].stdoutlog); } - while (util_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) { + while (file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) { /* * If a string contains an error we just dissalow execution * of it in the vm. @@ -942,11 +1056,11 @@ void task_schedualize() { task_tasks[i].compiled = false; } - fputs(data, task_tasks[i].stderrlog); + file_puts(task_tasks[i].stderrlog, data); fflush(task_tasks[i].stdoutlog); } - if (!task_tasks[i].compiled) { + if (!task_tasks[i].compiled && strcmp(task_tasks[i].template->proceduretype, "-fail")) { con_err("test failure: `%s` [%s] (failed to compile) see %s.stdout and %s.stderr\n", task_tasks[i].template->description, (task_tasks[i].template->failuremessage) ? @@ -1048,7 +1162,7 @@ bool test_perform(const char *curdir) { task_precleanup(curdir); if (!task_propagate(curdir)) { con_err("error: failed to propagate tasks\n"); - task_destroy(curdir); + task_destroy(); return false; } /* @@ -1059,7 +1173,7 @@ bool test_perform(const char *curdir) { * issues. */ task_schedualize(); - task_destroy(curdir); + task_destroy(); return true; }