}
#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
* 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.
*
*/
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)
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
/*
* 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;
}
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));
/*
* 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;
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;
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");
* 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
* 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
);
}
found
);
- closedir(dir);
+ fs_dir_close(dir);
return success;
}
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"))
}
}
- closedir(dir);
+ fs_dir_close(dir);
}
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))
/*
* 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.
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;
/*
* Copy to output vector for diagnostics if execution match
* fails.
- */
+ */
vec_push(*line, data);
/* reset */
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
*/
#include <math.h>
void task_schedualize(size_t *pad) {
+ char space[2][64];
bool execute = false;
char *data = NULL;
char **match = NULL;
size_t i = 0;
size_t j = 0;
+ snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
+
for (; i < vec_size(task_tasks); i++) {
- con_out("test #%u %*s", i + 1, (int)log10((double)vec_size(task_tasks)) - (int)(log10(i + 1)), "");
+ 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]), "");
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
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) {
/*
}
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("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("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("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)"
);
/*
* 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)
);
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)) ? "<<nothing else to compare>>" : match[d]);
+ con_out(" ");
+ con_out("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : 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]);
mem_d(match[j]);
vec_free(match);
- con_out("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);
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)) {