Scanner is program that crawles through given files and
copies out every definition/declaration marked by WL_EXPORT_TEST.
It saves these definitions into test-runner/tests-private.[ch]
so that these definition can be tested. Using the scanner guarantee testing of
current code of functions (contrary to manual copying from source file)
---
 src/wayland-util.h                |   3 +
 tests/test-runner/Makefile.am     |  15 ++
 tests/test-runner/scanner-tests.c | 349 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 367 insertions(+)
 create mode 100644 tests/test-runner/scanner-tests.c

diff --git a/src/wayland-util.h b/src/wayland-util.h
index 68d91e2..5df0a54 100644
--- a/src/wayland-util.h
+++ b/src/wayland-util.h
@@ -44,6 +44,9 @@ extern "C" {
 #define WL_EXPORT
 #endif
 
+/* export function for tests */
+#define WL_EXPORT_TEST
+
 /* Deprecated attribute */
 #if defined(__GNUC__) && __GNUC__ >= 4
 #define WL_DEPRECATED __attribute__ ((deprecated))
diff --git a/tests/test-runner/Makefile.am b/tests/test-runner/Makefile.am
index b8f1e76..bd7c292 100644
--- a/tests/test-runner/Makefile.am
+++ b/tests/test-runner/Makefile.am
@@ -9,5 +9,20 @@ libtest_runner_la_SOURCES =    \
 libtest_helpers_la_SOURCES =   \
        test-helpers.c
 
+scanner_input_files =  \
+       scanner-tests.c
+
+noinst_PROGRAMS = scanner-tests
+scanner_tests_SOURCES = scanner-tests.c
+
+$(BUILT_SOURCES) : scanner-tests $(scanner_input_files)
+       $(AM_V_GEN)./scanner-tests $(scanner_input_files)
+
+BUILT_SOURCES = \
+       tests-private.c \
+       tests-private.h
+
 AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src
 AM_CFLAGS = $(GCC_CFLAGS)
+
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/tests/test-runner/scanner-tests.c 
b/tests/test-runner/scanner-tests.c
new file mode 100644
index 0000000..427e47e
--- /dev/null
+++ b/tests/test-runner/scanner-tests.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright © 2013 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+#include <time.h>
+
+/* In order to make static functions testable too */
+#define WL_EXPORT_TEST
+
+WL_EXPORT_TEST
+const char *output = "tests-private.c";
+WL_EXPORT_TEST
+const char *output_header = "tests-private.h";
+
+WL_EXPORT_TEST
+static void
+extract_preprocessor(FILE *in, FILE *outc, FILE *outh)
+{
+       char name[8];
+       int chr, pchr = 0;
+
+       /* # was read, copy only directive */
+       fscanf(in, "%7s", name);
+
+       if (strcmp(name, "include") == 0) {
+               fprintf(outc, "#include ");
+               fprintf(outh, "#include ");
+               while ((chr = getc(in)) != EOF && chr !='\n') {
+                       putc(chr, outc);
+                       putc(chr, outh);
+               }
+       } else if (strcmp(name, "define") == 0) {
+               fprintf(outc, "\n#define ");
+               fprintf(outh, "\n#define ");
+               while ((chr = getc(in)) != EOF) {
+                       if (chr == '\n' && pchr != '\\')
+                               break;
+
+                       putc(chr, outc);
+                       putc(chr, outh);
+                       pchr = chr;
+               }
+       }
+}
+
+WL_EXPORT_TEST
+static bool
+is_test_export(FILE *f)
+{
+       int chr;
+       unsigned i;
+       const char pattern[] = "WL_EXPORT_TEST";
+
+       /* W is already captured */
+       i = 1;
+
+       while ((chr = getc(f)) != EOF && i < strlen(pattern)) {
+               if (chr != pattern[i++])
+                       return false;
+       }
+
+       /* did we go through the entire pattern? */
+       if (i != strlen(pattern))
+               return false;
+
+       return true;
+}
+
+WL_EXPORT_TEST
+static void *
+reallocate(void *mem, size_t *alloced)
+{
+       *alloced *= 2;
+       mem = realloc(mem, *alloced);
+       if (!mem)
+               abort();
+
+       return mem;
+}
+
+WL_EXPORT_TEST
+struct definition {
+       char *str;
+       enum {
+               NONE = 0,
+               VAR_DEF = 1,
+               VAR_DECL = 2,
+               FUNC = 3
+       } type;
+       int par; /* position of terminating parenthesis in prototype */
+};
+
+WL_EXPORT_TEST
+static struct definition
+extract_definition(FILE *in)
+{
+       int chr, pchr = 0;
+       bool got_body = false;
+       bool par = false; /* got parenthesis */
+       bool eq = false;
+       bool instring = false; /* set to true if inside of "" */
+       bool inquotes = false; /* '' */
+       bool incomment = false;
+       int braces = 0;
+       struct definition d = {NULL, NONE, 0};
+       size_t alloced = 500;
+       size_t i = 0;
+
+       d.str = malloc(alloced);
+       if (!d.str)
+               abort();
+
+       while ((chr = getc(in)) != EOF) {
+               switch (chr) {
+               /* handle comments */
+               case '*':
+                       if (pchr == '/')
+                               incomment = true;
+                       break;
+               case '/':
+                       if (pchr == '*')
+                               incomment = false;
+                       break;
+               /* handle strings and characters */
+               case '"':
+                       if (pchr == '\\' || inquotes)
+                               break;
+
+                       instring = instring ? false : true;
+                       break;
+               case '\'':
+                       if ((pchr == '\\' && d.str[i - 2] != '\\') || instring)
+                               break;
+
+                       inquotes = inquotes ? false : true;
+                       break;
+               /* count nesting */
+               case '{':
+                       if (inquotes || instring || incomment)
+                               break;
+
+                       if (!got_body)
+                               got_body = true;
+
+                       braces++;
+                       break;
+               case '}':
+                       if (inquotes || instring || incomment)
+                               break;
+
+                       braces--;
+                       break;
+               case '=':
+                       /* distinguish declaration from definition */
+                       if (!got_body)
+                               eq = true;
+                       break;
+               case ';':
+                       if (got_body)
+                               break;
+
+                       /* variable definition/declaration */
+                       /* here we can end */
+                       d.str[i] = chr;
+                       d.type = eq ? VAR_DEF : VAR_DECL;
+
+                       if (i == alloced)
+                               d.str = reallocate(d.str, &alloced);
+                       d.str[i + 1] = 0;
+
+                       return d;
+               case '(':
+                       par = true;
+                       break;
+               case ')':
+                       /* save index of the prototype termination,
+                        * it will have a use later */
+                       if (!braces)
+                               d.par = i;
+                       break;
+               }
+
+               d.str[i] = chr;
+               i++;
+
+               /* remove static if present */
+               if (i >= 6 && !got_body &&
+                   strncmp(d.str + i - 6, "static", 6) == 0) {
+                       /* shift index so that static will be rewritten */
+                       i -= 6;
+               }
+
+               if (braces == 0 && got_body)
+                       break;
+
+               if (i == alloced)
+                       d.str = reallocate(d.str, &alloced);
+
+               pchr = chr;
+       }
+
+       /* copy whatever is after last '}' */
+       if (got_body) {
+               while ((chr = getc(in)) != EOF && chr != '\n') {
+
+                       /* struct decl or definition.
+                        * Can be in .h file */
+                       if (chr == ';')
+                               d.type = VAR_DECL;
+
+                       d.str[i] = chr;
+
+                       i++;
+                       if (i == alloced) {
+                               d.str = reallocate(d.str, &alloced);
+                       }
+               }
+       }
+
+       d.str[i] = 0;
+       if (d.type == NONE)
+               d.type = FUNC;
+
+       return d;
+}
+
+static void
+write_header(FILE *out, const char *file)
+{
+       time_t t;
+       t = time(NULL);
+
+       fprintf(out,
+               "\n"
+               "/* -----------------------------------------------------------"
+                       "--------\n"
+               " * Following code was copied from %s\n"
+               " * %s"
+               " * -----------------------------------------------------------"
+                       "----- */\n",
+               file, ctime(&t));
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+       int chr;
+       bool newline = true;
+       FILE *fi, *fo, *fh;
+       struct definition d;
+       const char *def;
+
+       if (argc < 2)
+               errx(EXIT_FAILURE, "Usage: scanner-tests file1.c file2.c ...");
+
+       /* rewrite any old file */
+       fo = fopen(output, "wt");
+       if (!fo)
+               err(EXIT_FAILURE, "Failed creating file '%s'", output);
+       fh = fopen(output_header, "wt");
+       if (!fh)
+               err(EXIT_FAILURE, "Failed creating file '%s'",
+                   output_header);
+
+       /* include header into C file */
+       fprintf(fo, "#include \"%s\"\n", output_header);
+
+       for (i = 1; i < argc;  i++) {
+               fi = fopen(argv[i], "rt");
+               if (!fi)
+                       err(EXIT_FAILURE, "Failed opening file '%s'", argv[i]);
+
+               write_header(fo, argv[i]);
+               write_header(fh, argv[i]);
+
+               while ((chr = getc(fi)) != EOF) {
+                       if (chr == '#' && newline) {
+                               extract_preprocessor(fi, fo, fh);
+                               putc('\n', fo);
+                               putc('\n', fh);
+                               continue;
+                       } else if (chr == 'W' && newline && is_test_export(fi)) 
{
+                               d = extract_definition(fi);
+
+                               /* skip whitespaces if there is any */
+                               def = d.str;
+                               while (def && isspace(*def))
+                                       def++;
+
+                               switch(d.type) {
+                               case VAR_DEF:
+                                       fprintf(fo, "%s\n", def);
+                                       break;
+                               case VAR_DECL:
+                                       fprintf(fh, "%s\n", def);
+                                       break;
+                               case FUNC:
+                                       fprintf(fo, "%s\n\n", def);
+                                       d.str[d.par + 1] = 0;
+                                       fprintf(fh, "%s;\n", def);
+                                       break;
+                               case NONE:
+                                       fprintf(stderr, "not handled:\n%s\n",
+                                               d.str);
+                               }
+
+                               free(d.str);
+                       }
+
+                       /* ignore whitspaces on the begining of lines */
+                       if (chr == '\n')
+                               newline = true;
+
+                       if (newline && !isspace(chr))
+                               newline = false;
+               }
+
+               fclose(fi);
+       }
+
+       fclose(fh);
+       fclose(fo);
+
+       return 0;
+}
-- 
1.8.4.2

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to