X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=test.c;h=2ede2ae2c14c2ed4a2607da768f6138324e37a20;hp=0ddb75a70cbd021bec1c17629f390416f59ad75d;hb=160e7cf7eebd7fa173fb739aca00143097a3518b;hpb=b5ead871efa9190a8bfe3b56458bb3b0d65cab8b diff --git a/test.c b/test.c index 0ddb75a..2ede2ae 100644 --- a/test.c +++ b/test.c @@ -176,8 +176,8 @@ int task_pclose(FILE **handles) { } #endif /*! _WIN32 */ -#define TASK_COMPILE 0 -#define TASK_EXECUTE 1 +#define TASK_COMPILE 0 +#define TASK_EXECUTE 1 /* * Task template system: * templates are rules for a specific test, used to create a "task" that @@ -210,7 +210,7 @@ int task_pclose(FILE **handles) { * This will perform compilation and execution * -fail * This will perform compilation, but requires - * the compilation to fail in order to succeed. + * the compilation to fail in order to succeed. * * This must be provided, this tag is NOT optional. * @@ -269,6 +269,7 @@ typedef struct { */ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) { size_t desclen = 0; + size_t filelen = 0; char **destval = NULL; if (!tmpl) @@ -315,7 +316,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s if (strchr(value, '\n')) *strrchr(value, '\n')='\0'; else /* cppcheck: possible nullpointer dereference */ - abort(); + exit(EXIT_FAILURE); /* * Now allocate and set the actual value for the specific tag. Which @@ -328,11 +329,14 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s /* * Create some padding for the description to align the * printing of the rules file. - */ + */ if ((desclen = strlen(tmpl->description)) > pad[0]) pad[0] = desclen; } + if ((filelen = strlen(file)) > pad[2]) + pad[2] = filelen; + return true; } @@ -428,7 +432,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size if (strrchr(value, '\n')) *strrchr(value, '\n')='\0'; else /* cppcheck: possible null pointer dereference */ - abort(); + exit(EXIT_FAILURE); vec_push(tmpl->comparematch, util_strdup(value)); @@ -493,7 +497,7 @@ task_template_t *task_template_compile(const char *file, const char *dir, size_t /* * Create some padding for the printing to align the * printing of the rules file to the console. - */ + */ if ((filepadd = strlen(fullfile)) > pad[1]) pad[1] = filepadd; @@ -563,7 +567,13 @@ task_template_t *task_template_compile(const char *file, const char *dir, size_t con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file); if (tmpl->comparematch) con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file); - goto success; + } else if (!strcmp(tmpl->proceduretype, "-pp")) { + if (tmpl->executeflags) + con_err("template compile warning: %s erroneous tag `E:` when only preprocessing\n", file); + if (!tmpl->comparematch) { + con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file); + goto failure; + } } else { con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype); goto failure; @@ -644,10 +654,10 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { char buffer[4096]; size_t found = 0; - dir = opendir(curdir); + dir = fs_dir_open(curdir); - while ((files = readdir(dir))) { - snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name); + while ((files = fs_dir_read(dir))) { + snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name); if (stat(buffer, &directory) == -1) { con_err("internal error: stat failed, aborting\n"); @@ -679,7 +689,8 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { * Generate a temportary file name for the output binary * so we don't trample over an existing one. */ - tmpl->tempfilename = tempnam(curdir, "TMPDAT"); + tmpl->tempfilename = NULL; + util_asprintf(&tmpl->tempfilename, "%s/TMPDAT.%s", curdir, files->d_name); /* * Additional QCFLAGS enviroment variable may be used @@ -693,45 +704,66 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { * which will be refered to with a handle in the task for * reading the data from the pipe. */ - if (qcflags) { - if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { - snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s", - task_bins[TASK_COMPILE], - curdir, - tmpl->sourcefile, - qcflags, - tmpl->compileflags, - tmpl->tempfilename - ); + if (strcmp(tmpl->proceduretype, "-pp")) { + if (qcflags) { + if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { + snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + tmpl->sourcefile, + qcflags, + tmpl->compileflags, + tmpl->tempfilename + ); + } else { + snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + defs, + curdir, + tmpl->sourcefile, + qcflags, + tmpl->compileflags, + tmpl->tempfilename + ); + } } else { - snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s", - task_bins[TASK_COMPILE], - curdir, - defs, - curdir, - tmpl->sourcefile, - qcflags, - tmpl->compileflags, - tmpl->tempfilename - ); + if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { + snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + tmpl->sourcefile, + tmpl->compileflags, + tmpl->tempfilename + ); + } else { + snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s", + task_bins[TASK_COMPILE], + curdir, + defs, + curdir, + tmpl->sourcefile, + tmpl->compileflags, + tmpl->tempfilename + ); + } } } else { + /* Preprocessing (qcflags mean shit all here we don't allow them) */ if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { - snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s", + snprintf(buf, sizeof(buf), "%s -E %s/%s -o %s", task_bins[TASK_COMPILE], curdir, tmpl->sourcefile, - tmpl->compileflags, tmpl->tempfilename ); } else { - snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s", + snprintf(buf, sizeof(buf), "%s -E %s/%s %s/%s -o %s", task_bins[TASK_COMPILE], curdir, defs, curdir, tmpl->sourcefile, - tmpl->compileflags, tmpl->tempfilename ); } @@ -777,7 +809,7 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) { found ); - closedir(dir); + fs_dir_close(dir); return success; } @@ -790,9 +822,9 @@ void task_precleanup(const char *curdir) { struct dirent *files; char buffer[4096]; - dir = opendir(curdir); + dir = fs_dir_open(curdir); - while ((files = readdir(dir))) { + while ((files = fs_dir_read(dir))) { if (strstr(files->d_name, "TMP") || strstr(files->d_name, ".stdout") || strstr(files->d_name, ".stderr")) @@ -805,7 +837,7 @@ void task_precleanup(const char *curdir) { } } - closedir(dir); + fs_dir_close(dir); } void task_destroy(void) { @@ -827,7 +859,7 @@ void task_destroy(void) { /* * Only remove the log files if the test actually compiled otherwise * forget about it (or if it didn't compile, and the procedure type - * was set to -fail (meaning it shouldn't compile) .. stil remove) + * was set to -fail (meaning it shouldn't compile) .. stil remove) */ if (task_tasks[i].compiled || !strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) { if (remove(task_tasks[i].stdoutlogfile)) @@ -854,40 +886,50 @@ void task_destroy(void) { /* * This executes the QCVM task for a specificly compiled progs.dat * using the template passed into it for call-flags and user defined - * messages. + * messages IF the procedure type is -execute, otherwise it matches + * the preprocessor output. */ -bool task_execute(task_template_t *tmpl, char ***line) { +bool task_trymatch(task_template_t *tmpl, char ***line) { bool success = true; FILE *execute; char buffer[4096]; memset (buffer,0,sizeof(buffer)); - /* - * Drop the execution flags for the QCVM if none where - * actually specified. - */ - if (!strcmp(tmpl->executeflags, "$null")) { - snprintf(buffer, sizeof(buffer), "%s %s", - task_bins[TASK_EXECUTE], - tmpl->tempfilename + if (strcmp(tmpl->proceduretype, "-pp")) { + /* + * Drop the execution flags for the QCVM if none where + * actually specified. + */ + if (!strcmp(tmpl->executeflags, "$null")) { + snprintf(buffer, sizeof(buffer), "%s %s", + task_bins[TASK_EXECUTE], + tmpl->tempfilename + ); + } else { + snprintf(buffer, sizeof(buffer), "%s %s %s", + task_bins[TASK_EXECUTE], + tmpl->executeflags, + tmpl->tempfilename + ); + } + + util_debug("TEST", "executing qcvm: `%s` [%s]\n", + tmpl->description, + buffer ); + + execute = popen(buffer, "r"); + if (!execute) + return false; } else { - snprintf(buffer, sizeof(buffer), "%s %s %s", - task_bins[TASK_EXECUTE], - tmpl->executeflags, - tmpl->tempfilename - ); + /* + * we're preprocessing, which means we need to read int + * the produced file and do some really weird shit. + */ + if (!(execute = fs_file_open(tmpl->tempfilename, "r"))) + return false; } - util_debug("TEST", "executing qcvm: `%s` [%s]\n", - tmpl->description, - buffer - ); - - execute = popen(buffer, "r"); - if (!execute) - return false; - /* * Now lets read the lines and compare them to the matches we expect * and handle accordingly. @@ -913,6 +955,13 @@ bool task_execute(task_template_t *tmpl, char ***line) { if (strrchr(data, '\n')) *strrchr(data, '\n') = '\0'; + /* + * If data is just null now, that means the line was an empty + * one and for that, we just ignore it. + */ + if (!*data) + continue; + if (vec_size(tmpl->comparematch) > compare) { if (strcmp(data, tmpl->comparematch[compare++])) success = false; @@ -923,7 +972,7 @@ bool task_execute(task_template_t *tmpl, char ***line) { /* * Copy to output vector for diagnostics if execution match * fails. - */ + */ vec_push(*line, data); /* reset */ @@ -933,33 +982,56 @@ bool task_execute(task_template_t *tmpl, char ***line) { mem_d(data); data = NULL; } - pclose(execute); + + if (strcmp(tmpl->proceduretype, "-pp")) + pclose(execute); + else + fs_file_close(execute); + return success; } +const char *task_type(task_template_t *tmpl) { + if (!strcmp(tmpl->proceduretype, "-pp")) + return "type: preprocessor"; + if (!strcmp(tmpl->proceduretype, "-execute")) + return "type: execution"; + if (!strcmp(tmpl->proceduretype, "-compile")) + return "type: compile"; + return "type: fail"; +} + /* * This schedualizes all tasks and actually runs them individually * this is generally easy for just -compile variants. For compile and * execution this takes more work since a task needs to be generated * from thin air and executed INLINE. */ +#include void task_schedualize(size_t *pad) { + char space[2][64]; bool execute = false; char *data = NULL; char **match = NULL; size_t size = 0; - size_t i; - size_t j; + size_t i = 0; + size_t j = 0; - util_debug("TEST", "found %d tasks, preparing to execute\n", vec_size(task_tasks)); + snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks)); + + for (; i < vec_size(task_tasks); i++) { + memset(space[1], 0, sizeof(space[1])); + snprintf(space[1], sizeof(space[1]), "%d", (int)(i + 1)); + + con_out("test #%u %*s", i + 1, strlen(space[0]) - strlen(space[1]), ""); - for (i = 0; i < vec_size(task_tasks); i++) { util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description); /* * Generate a task from thin air if it requires execution in * the QCVM. */ - execute = !!(!strcmp(task_tasks[i].tmpl->proceduretype, "-execute")); + execute = !! (!strcmp(task_tasks[i].tmpl->proceduretype, "-execute")) || + (!strcmp(task_tasks[i].tmpl->proceduretype, "-pp")); /* * We assume it compiled before we actually compiled :). On error @@ -978,8 +1050,6 @@ void task_schedualize(size_t *pad) { task_tasks[i].compiled = false; execute = false; } - - fs_file_flush(task_tasks[i].stdoutlog); } while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) { /* @@ -996,40 +1066,51 @@ void task_schedualize(size_t *pad) { } fs_file_puts (task_tasks[i].stderrlog, data); - fs_file_flush(task_tasks[i].stdoutlog); } if (!task_tasks[i].compiled && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) { - con_err("test failure: `%s` (failed to compile) see %s.stdout and %s.stderr [%s]\n", + con_out("failure: `%s` %*s %*s\n", task_tasks[i].tmpl->description, - task_tasks[i].tmpl->tempfilename, - task_tasks[i].tmpl->tempfilename, - task_tasks[i].tmpl->rulesfile + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), + task_tasks[i].tmpl->rulesfile, + (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen("(failed to compile)") - pad[2]), + "(failed to compile)" ); continue; } if (!execute) { - con_out("test succeeded: `%s` %*s\n", + con_out("succeeded: `%s` %*s %*s\n", task_tasks[i].tmpl->description, - (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + - (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), - task_tasks[i].tmpl->rulesfile - + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), + task_tasks[i].tmpl->rulesfile, + (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl)) - pad[2]), + task_type(task_tasks[i].tmpl) + ); continue; } /* * If we made it here that concludes the task is to be executed - * in the virtual machine. + * in the virtual machine (or the preprocessor output needs to + * be matched). */ - if (!task_execute(task_tasks[i].tmpl, &match)) { + if (!task_trymatch(task_tasks[i].tmpl, &match)) { size_t d = 0; - con_err("test failure: `%s` (invalid results from execution) [%s]\n", + con_out("failure: `%s` %*s %*s\n", task_tasks[i].tmpl->description, - task_tasks[i].tmpl->rulesfile + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), + task_tasks[i].tmpl->rulesfile, + (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen( + (strcmp(task_tasks[i].tmpl->proceduretype, "-pp")) + ? "(invalid results from execution)" + : "(invalid results from preprocessing)" + ) - pad[2]), + (strcmp(task_tasks[i].tmpl->proceduretype, "-pp")) + ? "(invalid results from execution)" + : "(invalid results from preprocessing)" ); /* @@ -1037,7 +1118,7 @@ void task_schedualize(size_t *pad) { * handler for the all the given matches in the template file and * what was actually returned from executing. */ - con_err(" Expected From %u Matches: (got %u Matches)\n", + con_out(" Expected From %u Matches: (got %u Matches)\n", vec_size(task_tasks[i].tmpl->comparematch), vec_size(match) ); @@ -1045,25 +1126,25 @@ void task_schedualize(size_t *pad) { char *select = task_tasks[i].tmpl->comparematch[d]; size_t length = 40 - strlen(select); - con_err(" Expected: \"%s\"", select); + con_out(" Expected: \"%s\"", select); while (length --) - con_err(" "); - con_err("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<>" : match[d]); + con_out(" "); + con_out("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<>" : match[d]); } /* * Print the non-expected out (since we are simply not expecting it) * This will help track down bugs in template files that fail to match * something. - */ + */ if (vec_size(match) > vec_size(task_tasks[i].tmpl->comparematch)) { for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].tmpl->comparematch); d++) { - con_err(" Expected: Nothing | Got: \"%s\"\n", + con_out(" Expected: Nothing | Got: \"%s\"\n", match[d + vec_size(task_tasks[i].tmpl->comparematch)] ); } } - + for (j = 0; j < vec_size(match); j++) mem_d(match[j]); @@ -1074,12 +1155,13 @@ void task_schedualize(size_t *pad) { mem_d(match[j]); vec_free(match); - con_out("test succeeded: `%s` %*s\n", + con_out("succeeded: `%s` %*s %*s\n", task_tasks[i].tmpl->description, - (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + - (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), - task_tasks[i].tmpl->rulesfile - + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), + task_tasks[i].tmpl->rulesfile, + (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl))- pad[2]), + task_type(task_tasks[i].tmpl) + ); } mem_d(data); @@ -1103,17 +1185,18 @@ GMQCC_WARN bool test_perform(const char *curdir, const char *defs) { static const char *default_defs = "defs.qh"; size_t pad[] = { - 0, 0 + /* test ### [succeed/fail]: `description` [tests/template.tmpl] [type] */ + 0, 0, 0 }; /* * If the default definition file isn't set to anything. We will * use the default_defs here, which is "defs.qc" - */ + */ if (!defs) { defs = default_defs; } - + task_precleanup(curdir); if (!task_propagate(curdir, pad, defs)) {