]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - test.c
Cleanups
[xonotic/gmqcc.git] / test.c
diff --git a/test.c b/test.c
index ca1add20faef4cbd93296ce2e2589c2c6213866c..0559e618c3f64b4b0f1f371a85b06a74f5ba4e03 100644 (file)
--- a/test.c
+++ b/test.c
 #include "gmqcc.h"
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <dirent.h>
 
-bool  opts_memchk = false;
-bool  opts_debug  = false;
+opts_cmd_t opts;
+
 char *task_bins[] = {
     "./gmqcc",
     "./qcvm"
@@ -50,7 +49,7 @@ char *task_bins[] = {
 #ifndef _WIN32
 #include <sys/types.h>
 #include <sys/wait.h>
-
+#include <dirent.h>
 #include <unistd.h>
 typedef struct {
     FILE *handles[3];
@@ -157,7 +156,120 @@ int task_pclose(FILE **handles) {
     return status;
 }
 #else
+#      define _WIN32_LEAN_AND_MEAN
+#      define popen  _popen
+#      define pclose _pclose
+#      include <windows.h>
+#   include <io.h>
+#      include <fcntl.h>
+    /*
+     * 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 <dirent.h>
+#      elif defined (_MSC_VER)
+        /* 
+         * 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 +313,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.
  *
@@ -521,6 +636,12 @@ 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;
@@ -769,9 +890,10 @@ void task_destroy(const char *curdir) {
 
         /*
          * 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
@@ -803,7 +925,7 @@ void task_destroy(const char *curdir) {
  * messages.
  */
 bool task_execute(task_template_t *template, char ***line) {
-    bool     success = false;
+    bool     success = true;
     FILE    *execute;
     char     buffer[4096];
     memset  (buffer,0,sizeof(buffer));
@@ -860,18 +982,22 @@ bool task_execute(task_template_t *template, char ***line) {
             if  (strrchr(data, '\n'))
                 *strrchr(data, '\n') = '\0';
 
-
-            /*
-             * We only care about the last line from the output for now
-             * implementing multi-line match is TODO.
-             */
-            success = !!!(strcmp(data, template->comparematch[compare++]));
+            if (vec_size(template->comparematch) > compare) {
+                if (strcmp(data, template->comparematch[compare++]))
+                    success = false;
+            } else {
+                    success = false;
+            }
 
             /*
              * Copy to output vector for diagnostics if execution match
              * fails.
              */  
             vec_push(*line, data);
+
+            /* reset */
+            data = NULL;
+            size = 0;
         }
         mem_d(data);
         data = NULL;
@@ -892,6 +1018,7 @@ void task_schedualize() {
     char **match    = NULL;
     size_t size     = 0;
     size_t i;
+    size_t j;
 
     util_debug("TEST", "found %d tasks, preparing to execute\n", vec_size(task_tasks));
 
@@ -901,8 +1028,7 @@ void task_schedualize() {
          * Generate a task from thin air if it requires execution in
          * the QCVM.
          */
-        if (!strcmp(task_tasks[i].template->proceduretype, "-execute"))
-            execute = true;
+        execute = !!(!strcmp(task_tasks[i].template->proceduretype, "-execute"));
 
         /*
          * We assume it compiled before we actually compiled :).  On error
@@ -942,7 +1068,7 @@ void task_schedualize() {
             fflush(task_tasks[i].stdoutlog);
         }
 
-        if (!execute) {
+        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) ?
@@ -952,6 +1078,16 @@ void task_schedualize() {
             );
             continue;
         }
+
+        if (!execute) {
+            con_out("test succeeded: `%s` [%s]\n",
+                 task_tasks[i].template->description,
+                (task_tasks[i].template->successmessage) ?
+                 task_tasks[i].template->successmessage  : "unknown"
+            );
+            continue;
+        }
+
         /*
          * If we made it here that concludes the task is to be executed
          * in the virtual machine.
@@ -970,7 +1106,10 @@ void task_schedualize() {
              * handler for the all the given matches in the template file and
              * what was actually returned from executing.
              */
-            con_err("    Expected From %u Matches:\n", vec_size(task_tasks[i].template->comparematch));
+            con_err("    Expected From %u Matches: (got %u Matches)\n",
+                vec_size(task_tasks[i].template->comparematch),
+                vec_size(match)
+            );
             for (; d < vec_size(task_tasks[i].template->comparematch); d++) {
                 char  *select = task_tasks[i].template->comparematch[d];
                 size_t length = 40 - strlen(select);
@@ -980,9 +1119,29 @@ void task_schedualize() {
                     con_err(" ");
                 con_err("| 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].template->comparematch)) {
+                for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].template->comparematch); d++) {
+                    con_err("        Expected: Nothing                                   | Got: \"%s\"\n",
+                        match[d + vec_size(task_tasks[i].template->comparematch)]
+                    );
+                }
+            }
+                    
+
+            for (j = 0; j < vec_size(match); j++)
+                mem_d(match[j]);
             vec_free(match);
             continue;
         }
+        for (j = 0; j < vec_size(match); j++)
+            mem_d(match[j]);
+        vec_free(match);
 
         con_out("test succeeded: `%s` [%s]\n",
              task_tasks[i].template->description,
@@ -1080,11 +1239,11 @@ int main(int argc, char **argv) {
             con_change(redirout, redirerr);
 
             if (!strcmp(argv[0]+1, "debug")) {
-                opts_debug = true;
+                opts.debug = true;
                 continue;
             }
             if (!strcmp(argv[0]+1, "memchk")) {
-                opts_memchk = true;
+                opts.memchk = true;
                 continue;
             }
             if (!strcmp(argv[0]+1, "nocolor")) {