diff options
author | Owen Taylor <otaylor@src.gnome.org> | 2008-11-11 05:10:56 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2008-11-11 05:10:56 +0000 |
commit | 98d1d8b9e0fcbfe798eff20ccce44df5af18854b (patch) | |
tree | e37ff6f833b84357303599b2452de7bbf6bee000 | |
parent | 0dccc4596aad89333f7d23b2f95a19067df79724 (diff) | |
download | gobject-introspection-98d1d8b9e0fcbfe798eff20ccce44df5af18854b.tar.gz |
Add tests for field offset computations
tests/offsets/offsets.h: Header file with structure definitions to test
tests/offsets/gen-gitestoffsets: Generate a C program that computes
field offsets for the structures in offsets.h two ways: using
the information from a generated typelib, and as computed by the
compiler.
We diff these two versions to test that everything is OK.
svn path=/trunk/; revision=878
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/offsets/Makefile.am | 62 | ||||
-rwxr-xr-x | tests/offsets/gen-gitestoffsets | 242 | ||||
-rw-r--r-- | tests/offsets/offsets.c | 7 | ||||
-rw-r--r-- | tests/offsets/offsets.h | 107 |
7 files changed, 433 insertions, 1 deletions
@@ -1,5 +1,18 @@ 2008-11-10 Owen Taylor <otaylor@redhat.com> + Add tests for field offset computations + + * tests/offsets/offsets.h: Header file with structure definitions to test + + * tests/offsets/gen-gitestoffsets: Generate a C program that computes + field offsets for the structures in offsets.h two ways: using + the information from a generated typelib, and as computed by the + compiler. + + We diff these two versions to test that everything is OK. + +2008-11-10 Owen Taylor <otaylor@redhat.com> + Remove field offsets from g-ir-generate output and test inputs Field offsets are a) architecture dependent so they shouldn't be part diff --git a/configure.ac b/configure.ac index da2160d8..40a6c2d9 100644 --- a/configure.ac +++ b/configure.ac @@ -194,6 +194,7 @@ m4/Makefile tools/Makefile tests/Makefile tests/invoke/Makefile +tests/offsets/Makefile tests/scanner/Makefile tests/repository/Makefile tests/everything/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 77484681..d77e4939 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . scanner everything repository invoke +SUBDIRS = . scanner everything repository invoke offsets EXTRA_DIST = \ array.gir \ diff --git a/tests/offsets/Makefile.am b/tests/offsets/Makefile.am new file mode 100644 index 00000000..456da610 --- /dev/null +++ b/tests/offsets/Makefile.am @@ -0,0 +1,62 @@ +BUILT_SOURCES = +CLEANFILES = +EXTRA_DIST = + +check_LTLIBRARIES = +check_PROGRAMS = + +SCANNER = $(top_srcdir)/tools/g-ir-scanner +SCANNER_PYTHONPATH = $(top_builddir):$(top_srcdir):$$PYTHONPATH +SCANNER_LIBS = \ + $(top_srcdir)/giscanner/*.py \ + $(top_builddir)/giscanner/libgiscanner.la + +############################################################ + +check_LTLIBRARIES += liboffsets.la + +liboffsets_la_SOURCES = \ + offsets.h \ + offsets.c +liboffsets_la_CPPFLAGS = $(GIREPO_CFLAGS) +# dummy rpath to get built dynamically (huh?) +liboffsets_la_LDFLAGS = -module -avoid-version -rpath $(libdir) + +offsets-1.0.gir: liboffsets.la offsets.h $(SCANNER) $(SCANNER_LIBS) Makefile + PYTHONPATH=$(SCANNER_PYTHONPATH) $(CHECK_DEBUG) $(SCANNER) -v \ + --add-include-path=$(top_builddir)/gir --add-include-path=. \ + --library=offsets \ + --namespace=offsets \ + --nsversion=1.0 \ + --pkg gobject-2.0 \ + $(srcdir)/offsets.h $(srcdir)/offsets.c \ + --output $@ + +%.typelib: %.gir $(top_builddir)/tools/g-ir-compiler$(EXEEXT) Makefile + $(top_builddir)/tools/g-ir-compiler --includedir=. --includedir=$(top_builddir)/gir $< -o $@ + +CLEANFILES += offsets-1.0.gir offsets-1.0.typelib + +############################################################ + +check_PROGRAMS += gitestoffsets + +nodist_gitestoffsets_SOURCES = gitestoffsets.c +gitestoffsets_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository +gitestoffsets_LDADD = $(top_builddir)/girepository/libgirepository.la $(GIREPO_LIBS) + +gitestoffsets.c: gen-gitestoffsets offsets.h + $(PYTHON) $(srcdir)/gen-gitestoffsets offsets.h > $@ || ( rm -f $@ && false ) + +EXTRA_DIST += gen-gitestoffsets +BUILT_SOURCES += gitestoffsets.c +CLEANFILES += gitestoffsets.c + +############################################################ + +check-local: offsets-1.0.typelib + LD_LIBRARY_PATH=$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}$(builddir) GI_TYPELIB_PATH=$(builddir) \ + $(builddir)/gitestoffsets$(EXEEXT) offsets.compiled offsets.introspected + diff -u offsets.compiled offsets.introspected + +CLEANFILES += offsets.compiled offsets.introspected diff --git a/tests/offsets/gen-gitestoffsets b/tests/offsets/gen-gitestoffsets new file mode 100755 index 00000000..27fff23b --- /dev/null +++ b/tests/offsets/gen-gitestoffsets @@ -0,0 +1,242 @@ +#!/bin/env python +# -*- Mode: Python -*- +# GObject-Introspection - a framework for introspecting GObject libraries +# Copyright (C) 2008 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +# The idea here is that we want to compare offset information two ways: +# +# 1) As generated by the compiler +# 2) As found in the typelib +# +# So we find all the structures in the input file (offsets.h), parse out +# fields within the structure and generate code that outputs the field +# offsets using G_STRUCT_OFFSET() to one file and the field offsets using +# the typelib to the another file. We can then diff the two files to see +# if they are the same + +import re +import sys + +if len(sys.argv) != 2: + print >>sys.stderr, "Usage: gen-gitestoffsets INPUT > OUTPUT" + sys.exit(1) + +# Helper function that we use to generate source. It does substitions +# from a dictionary, removes white space at the ends and removes a +# leading '|' from each line +STRIP_AROUND_RE = re.compile("^[ \t]*\n?(.*?)[ \t]*$", re.DOTALL) +STRIP_LEADER_RE = re.compile("^\|", re.MULTILINE) +def output(args, format): + format = STRIP_AROUND_RE.sub(r"\1", format) + format = STRIP_LEADER_RE.sub(r"", format) + sys.stdout.write(format % args) + +# +# Some regular expressions we use when parsing +# +TOKEN = "(?:[A-Za-z_][A-Za-z_0-9]+)" + +def compile_re(expr): + expr = expr.replace("TOKEN", TOKEN) + return re.compile(expr, re.VERBOSE) + +COMMENT_RE = compile_re("/\*([^*]+|\*[^/])*\*/") +STRUCT_DEF_RE = compile_re("struct\s+_(TOKEN)\s*{([^}]*)}") + +# This certainly can't handle all type declarations, but it only +# needs to handle the ones we use in the test cases +FIELD_RE = compile_re("^(?:const\s+)?TOKEN(?:[\s*]+)(TOKEN)\s*;$"); + + +input_f = open(sys.argv[1]) +input = input_f.read() +input_f.close() + +# Strip comments +input = COMMENT_RE.sub("", input) + +symbols = [] +symbol_fields = {} + +for m in STRUCT_DEF_RE.finditer(input): + symbol = m.group(1) + + fields = [] + + body = m.group(2) + for line in re.split("\n|\r\n", body): + line = line.strip() + if line == "": + continue + n = FIELD_RE.match(line) + if not n: + print sys.stderr, "Can't parse structure line '%s'" % line + sys.exit(1) + fields.append(n.group(1)) + + symbols.append(symbol) + symbol_fields[symbol] = fields + +# Sort for convenience +symbols.sort() + +output({}, r''' +| /* GENERATED FILE. DO NOT EDIT. See gen-gitestoffsets */ +| +|#include <errno.h> +|#include <stdio.h> +|#include <string.h> +|#include <glib.h> +|#include <girepository.h> +|#include "offsets.h" +| +|static GIRepository *repository; +|static const char *namespace = "offsets"; +|static const char *version = "1.0"; +| +|static void +|print_field_offset(FILE *outfile, +| GIStructInfo *struct_info, +| const gchar *name) +|{ +| gint i; +| gint n_fields = g_struct_info_get_n_fields (struct_info); +| for (i = 0; i < n_fields; i++) +| { +| GIFieldInfo *field_info = g_struct_info_get_field (struct_info, i); +| const char *field_name = g_base_info_get_name ((GIBaseInfo *)field_info); +| if (strcmp (field_name, name) == 0) +| { +| fprintf (outfile, "%%s %%d\n", name, g_field_info_get_offset (field_info)); +| g_base_info_unref ((GIBaseInfo *)field_info); +| return; +| } +| +| g_base_info_unref ((GIBaseInfo *)field_info); +| } +| +| g_error("Can't find field '%%s.%%s' in introspection information", +| g_base_info_get_name ((GIBaseInfo *)struct_info), name); +|} +| +''') + +for symbol in symbols: + fields = symbol_fields[symbol] + + output({'symbol' : symbol}, r''' +|static void +|compiled_%(symbol)s (FILE *outfile) +|{ +| fprintf (outfile, "%(symbol)s:\n"); +| + ''') + + for field in fields: + output({ 'field' : field, 'symbol' : symbol }, r''' +| fprintf (outfile, "%%s %%ld\n", "%(field)s", G_STRUCT_OFFSET(%(symbol)s, %(field)s)); + ''') + + output({}, r''' +| +| fprintf (outfile, "\n"); +|} +| + ''') + + if not symbol.startswith("Offsets"): + print >> sys.stderr, "Symbol '%s' doesn't start with Offsets" % symbol + bare_symbol = symbol[len("Offsets"):] + + + output({'symbol' : symbol, 'bare_symbol' : bare_symbol}, r''' +|static void +|introspected_%(symbol)s (FILE *outfile) +|{ +| GIStructInfo *struct_info = (GIStructInfo *)g_irepository_find_by_name(repository, namespace, +| "%(bare_symbol)s"); +| if (!struct_info) +| g_error ("Can't find GIStructInfo for '%(symbol)s'"); +| +| fprintf (outfile, "%(symbol)s:\n"); +| + ''') + for field in fields: + output({'field' : field}, ''' +| print_field_offset(outfile, struct_info, "%(field)s"); + ''') + + output({}, r''' +| +| fprintf (outfile, "\n"); +| +| g_base_info_unref ((GIBaseInfo *)struct_info); +|} + ''') + +output({}, r''' +| +|int main(int argc, char **argv) +|{ +| GError *error = NULL; +| FILE *outfile; +| +| if (argc != 3) +| g_error ("Usage: gitestoffsets COMPILED_OFFSETS_FILE INTROSPECTED_OFFSETS_FILE"); +| +| g_type_init (); +| +| repository = g_irepository_get_default (); +| if (!g_irepository_require (repository, namespace, version, 0, &error)) +| g_error ("Failed to load %%s-%%s.typelib: %%s", namespace, version, error->message); +| +| outfile = fopen (argv[1], "w"); +| if (!outfile) +| g_error ("Cannot open '%%s': %%s'", argv[1], g_strerror(errno)); +| +''') + +for symbol in symbols: + output({'symbol' : symbol}, ''' +| compiled_%(symbol)s (outfile); + ''') + +output({}, ''' +| +| fclose (outfile); +| +| outfile = fopen (argv[2], "w"); +| if (!outfile) +| g_error ("Cannot open '%%s': %%s'", argv[1], g_strerror(errno)); +| +''') + + +for symbol in symbols: + output({'symbol' : symbol}, ''' +| introspected_%(symbol)s (outfile); +''') + +output({}, r''' +| +| fclose (outfile); +| +| return 0; +} +''') diff --git a/tests/offsets/offsets.c b/tests/offsets/offsets.c new file mode 100644 index 00000000..bd009e88 --- /dev/null +++ b/tests/offsets/offsets.c @@ -0,0 +1,7 @@ +#include <offsets.h> + +void offsets_dummy(void); + +/* To avoid an empty compilation unit */ +void offsets_dummy(void) { +} diff --git a/tests/offsets/offsets.h b/tests/offsets/offsets.h new file mode 100644 index 00000000..c0edfe01 --- /dev/null +++ b/tests/offsets/offsets.h @@ -0,0 +1,107 @@ +#ifndef __OFFSETS_H__ +#define __OFFSETS_H__ + +#include <glib.h> +#include <time.h> + +/* Test we get the alignment right for various basic types; we put + * a char in front of of each field to make it more likely that we'll + * stress out the alignment code. + */ +typedef struct _OffsetsBasic OffsetsBasic; + +struct _OffsetsBasic { + char dummy1; + gint8 field_int8; + char dummy2; + gint16 field_int16; + char dummy3; + gint32 field_int32; + char dummy4; + gint64 field_int64; + char dummy5; + gchar *field_pointer; + char dummy6; + gfloat field_float; + char dummy7; + gdouble field_double; + char dummy8; + gsize field_size; + char dummy9; + time_t field_time; +}; + +typedef enum { + ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */ +} Enum1; + +typedef enum { + ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */ +} Enum2; + +typedef enum { + ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */ +} Enum3; + +typedef enum { + ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */ +} Enum4; + +typedef enum { + ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */ +} Enum5; + +typedef enum { + ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */ +} Enum6; + +/* Test that we get the width of enum fields right. The char after + * each field will have aligment 1 (almost certainly) so should + * be placed right the field. + */ +typedef struct _OffsetsEnum OffsetsEnum; + +struct _OffsetsEnum { + Enum1 enum1; + char dummy1; + Enum2 enum2; + char dummy2; + Enum3 enum3; + char dummy3; + Enum4 enum4; + char dummy4; + Enum5 enum5; + char dummy5; + Enum6 enum6; + char dummy6; +}; + +/* Test nested structures + */ + +typedef struct _OffsetsNestee OffsetsNestee; + +struct _OffsetsNestee { + char field1; + double field2; /* alignment of structure is greater than its first element */ + char field3; /* structure has tail padding */ +}; + +typedef union _OffsetsNesteeUnion OffsetsNesteeUnion; + +union _OffsetsNesteeUnion { + char field1; + double field2; +}; + +typedef struct _OffsetsNested OffsetsNested; + +struct _OffsetsNested { + char dummy1; + OffsetsNestee nestee; + char dummy2; + OffsetsNesteeUnion nestee_union; + char dummy3; +}; + +#endif /* __OFFSETS_H__ */ |