From: Nick Bowler Date: Thu, 6 Feb 2020 05:05:59 +0000 (-0500) Subject: Expand the interface implementation test cases. X-Git-Url: https://git.draconx.ca/gitweb/gob-dx.git/commitdiff_plain/0c92ab0b40d0c819fe88edd88db5dbf4f609d6da Expand the interface implementation test cases. Test interface implementation for both dynamic and static types, and test implementing the same interface in two classes, when one class derives from another. The cases where a dynamic class derives from another and both implement the same interface are currently xfailing, as this functionality is actually busted. --- diff --git a/Makefile.am b/Makefile.am index 8ed1bdb..7f09957 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,8 @@ EXTRA_DIST = COPYING.GPL3 COPYING.generated-code \ examples/foo-some-interface.gob examples/gtk-button-count.gob \ examples/my-glade-main.c examples/my-glade.glade \ examples/my-glade.gob gob2.spec src/generate_treefuncs.pl \ - src/treefuncs.def src/treefuncs.stamp t/str.gob t/test.gob + src/treefuncs.def src/treefuncs.stamp t/str.gob t/test-fooable.c \ + t/test-fooable.h t/test.gob CLEANFILES = DISTCLEANFILES = diff --git a/t/test-fooable.c b/t/test-fooable.c new file mode 100644 index 0000000..d4b2d30 --- /dev/null +++ b/t/test-fooable.c @@ -0,0 +1,38 @@ +/* + * Copyright © 2020 Nick Bowler + * + * 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, see . + */ + +#include "test-fooable.h" + +GType test_fooable_get_type(void) +{ + static const GTypeInfo info = { sizeof (TestFooableIface), 0, 0 }; + static GType type = 0; + + if (!type) { + type = g_type_register_static(G_TYPE_INTERFACE, + "TestFooable", &info, 0); + } + + return type; +} + +int test_foo(GObject *go) +{ + g_return_val_if_fail(TEST_IS_FOOABLE(go), -1); + + return TEST_FOOABLE_GET_INTERFACE(go)->foo(go); +} diff --git a/t/test-fooable.h b/t/test-fooable.h new file mode 100644 index 0000000..e6ad847 --- /dev/null +++ b/t/test-fooable.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2020 Nick Bowler + * + * 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, see . + */ + +#ifndef TEST_FOOABLE_H_ +#define TEST_FOOABLE_H_ + +#include + +#define TEST_TYPE_FOOABLE (test_fooable_get_type()) +#define TEST_FOOABLE(obj) \ + G_TYPE_CHECK_INSTANCE_CAST(obj, TEST_TYPE_FOOABLE, TestFooableIface) +#define TEST_IS_FOOABLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, TEST_TYPE_FOOABLE) +#define TEST_FOOABLE_GET_INTERFACE(inst) \ + G_TYPE_INSTANCE_GET_INTERFACE(inst, TEST_TYPE_FOOABLE, TestFooableIface) + +typedef struct TestFooableIface TestFooableIface; +struct TestFooableIface { + GTypeInterface parent; + + int (*foo)(GObject *obj); +}; + +GType test_fooable_get_type(void); +int test_foo(GObject *go); + +#endif diff --git a/tests/general.at b/tests/general.at index 4001942..fa465e5 100644 --- a/tests/general.at +++ b/tests/general.at @@ -172,101 +172,6 @@ AT_CHECK([./main], [0], [Hello, World AT_CLEANUP -dnl Dynamic types: check that we can call interface methods of dynamic types. -AT_SETUP([dynamic interface implementation]) -AT_KEYWORDS([dynamic runtime interface]) - -AT_DATA([iface.h], -[[#define IF_TYPE_TEST if_test_get_type() - -typedef struct IFTestIface { - GTypeInterface parent; - - void (*test)(GObject *obj); -} IFTestIface; - -GType if_test_get_type(void); -]]) - -AT_DATA([test.gob], -[[%{ -#include -#include "iface.h" -%} - -class :Test from G:Object (dynamic) - (interface IF:Test) -{ - private const char *s = { "Hello, World!" }; - interface IF:Test private void test(G:Object *o) - { - Self *self = SELF(o); - printf("%s\n", self->_priv->s); - } -} -]]) - -AT_DATA([mod.gob], -[[%{ -#include "test.h" -%} -class :Mod from G:Type:Module -{ - override (G:Type:Module) gboolean load(G:Type:Module *m) - { - test_register_type(m); - return TRUE; - } -} -]]) - -AT_DATA([main.c], -[[#include "test.h" -#include "mod.h" -#include "iface.h" - -GType if_test_get_type(void) -{ - static GType type = 0; - if (type == 0) { - static const GTypeInfo info = { - sizeof (IFTestIface), - NULL, - NULL, - }; - - type = g_type_register_static(G_TYPE_INTERFACE, "IFTest", &info, 0); - } - return type; -} - -int main(void) -{ - GTypeModule *m = g_object_new(TYPE_MOD, NULL); - GObject *t; - - g_type_module_use(m); - t = g_object_new(TYPE_TEST, NULL); - - g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE(t, IF_TYPE_TEST), - EXIT_FAILURE); - G_TYPE_INSTANCE_GET_INTERFACE(t, IF_TYPE_TEST, IFTestIface)->test(t); - - return EXIT_SUCCESS; -} -]]) - -AT_CHECK([gob2 mod.gob]) -AT_CHECK([gob2 test.gob]) -TEST_COMPILE_GOBJECT([mod.c], [0], [], [ignore]) -TEST_COMPILE_GOBJECT([test.c], [0], [], [ignore]) -TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) -AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main mod.o test.o main.o]) -AT_CHECK([./main], [0], [Hello, World! -]) - -AT_CLEANUP - AT_SETUP([GOB2_CHECK min-version test]) AT_DATA([configure.ac], diff --git a/tests/interface.at b/tests/interface.at new file mode 100644 index 0000000..82771e1 --- /dev/null +++ b/tests/interface.at @@ -0,0 +1,297 @@ +dnl Copyright © 2020 Nick Bowler +dnl License GPLv2+: GNU General Public License version 2 or any later version. +dnl This is free software: you are free to change and redistribute it. +dnl There is NO WARRANTY, to the extent permitted by law. + +dnl TEST_TYPE_MODULE(Class:Name) +dnl Create a GTypeModule (Class:Name:Mod) which registers the dynamic +dnl type indiciated by Class:Name. +m4_define([TEST_TYPE_MODULE], +[TEST_TYPE_MODULE_([$1], m4_translit([[$1]], [:A-Z], [_a-z]), + m4_translit([[$1]], [:A-Z], [-a-z]))]) + +m4_define([TEST_TYPE_MODULE_], +[AT_KEYWORDS([dynamic])dnl + +AT_DATA([$3-mod.gob], [[%{ +#include "$3.h" +%} +class $1:Mod from G:Type:Module +{ + override (G:Type:Module) gboolean load(G:Type:Module *m) + { + $2_register_type(m); + return TRUE; + } +} +]]) +AT_CHECK([gob2 $3-mod.gob]) +TEST_COMPILE_GOBJECT([$3-mod.c], [0], [], [ignore])]) + +dnl Create the Test:Fooable interface with the following interface method: +dnl +dnl int foo(G:Object *obj); +dnl +dnl Link in test-fooable.o +m4_define([TEST_FOOABLE_IFACE], +[AT_KEYWORDS([interface])dnl +AT_CHECK([cp $srcdir/t/test-fooable.c $srcdir/t/test-fooable.h .]) +TEST_COMPILE_GOBJECT([test-fooable.c], [0], [], [ignore])]) + +dnl TEST_FOOABLE_IMPL(Class:Name, Parent:Class, foo_body) +dnl TEST_FOOABLE_IMPL_DYN(Class:Name, Parent:Class, foo_body) +m4_define([TEST_FOOABLE_IMPL], + [TEST_FOOABLE_IMPL_([$1], m4_translit([[$1]], [:A-Z], [-a-z]), + [$2], m4_translit([[$2]], [:A-Z], [-a-z]), + [$3], [$4])]) + +m4_define([TEST_FOOABLE_IMPL_DYN], +[TEST_FOOABLE_IMPL([$1], [$2], [$3], [(dynamic)]) +TEST_TYPE_MODULE([$1])]) + +m4_define([TEST_FOOABLE_IMPL_], +[AT_DATA([$2.gob], [[%{ +#include +#include +#include "test-fooable.h" +%} +]m4_if([$4], [g-object], [], [[ +%h{ +#include "$4.h" +%} +]])[ +class $1 from $3]m4_default_nblank([ + $6])[ + (interface Test:Fooable) +{ + interface Test:Fooable private int foo(G:Object *go) + { + $5 + abort(); + } +} +]]) +AT_CHECK([gob2 $2.gob]) +TEST_COMPILE_GOBJECT([$2.c], [0], [], [ignore])]) + +dnl Test that a static type can implement an interface. +AT_SETUP([interface implementation]) +AT_KEYWORDS([runtime])dnl + +TEST_FOOABLE_IFACE() +TEST_FOOABLE_IMPL([Test:A], [G:Object], [return 42;]) + +AT_DATA([main.c], +[[#include +#include +#include "test-fooable.h" +#include "test-a.h" + +int main(void) +{ + int rc; + + rc = test_foo(g_object_new(TEST_TYPE_A, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + return 0; +} +]]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) + +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \ + test-a.o test-fooable.o main.o]) +AT_CHECK([./main], [0], [42 +]) + +AT_CLEANUP + +dnl Test that a dynamic type can implement an interface. +AT_SETUP([interface implementation (dynamic)]) +AT_KEYWORDS([runtime])dnl + +TEST_FOOABLE_IFACE() +TEST_FOOABLE_IMPL_DYN([Test:A], [G:Object], [return 54;]) + +AT_DATA([main.c], +[[#include +#include +#include "test-fooable.h" +#include "test-a.h" +#include "test-a-mod.h" + +int main(void) +{ + int rc; + + g_type_module_use(g_object_new(TEST_TYPE_A_MOD, NULL)); + + rc = test_foo(g_object_new(TEST_TYPE_A, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + return 0; +} +]]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) + +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \ + test-a.o test-a-mod.o test-fooable.o main.o]) +AT_CHECK([./main], [0], [54 +]) + +AT_CLEANUP + +dnl Test that a static type can override the interface implementation +dnl in the parent type. +AT_SETUP([interface method override]) +AT_KEYWORDS([runtime])dnl + +TEST_FOOABLE_IFACE() +TEST_FOOABLE_IMPL([Test:A], [G:Object], + [puts("Test:A foo called"); return 42;]) +TEST_FOOABLE_IMPL([Test:B], [Test:A], + [puts("Test:B foo called"); return 54;]) + +AT_DATA([main.c], +[[#include +#include +#include "test-fooable.h" +#include "test-a.h" +#include "test-b.h" + +int main(void) +{ + int rc; + + rc = test_foo(g_object_new(TEST_TYPE_A, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + rc = test_foo(g_object_new(TEST_TYPE_B, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + return 0; +} +]]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) + +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \ + test-a.o test-b.o test-fooable.o main.o]) +AT_CHECK([./main], [0], [Test:A foo called +42 +Test:B foo called +54 +]) + +AT_CLEANUP + +dnl Test that a dynamic type can override the interface implementation of +dnl a static parent type. +AT_SETUP([interface method override (dynamic)]) +AT_KEYWORDS([runtime])dnl + +TEST_FOOABLE_IFACE() +TEST_FOOABLE_IMPL([Test:A], [G:Object], + [puts("Test:A foo called"); return 42;]) +TEST_FOOABLE_IMPL_DYN([Test:B], [Test:A], + [puts("Test:B foo called"); return 54;]) + +AT_DATA([main.c], +[[#include +#include "test-fooable.h" +#include "test-a.h" +#include "test-b.h" +#include "test-b-mod.h" + +int main(void) +{ + int rc; + + g_type_module_use(g_object_new(TEST_TYPE_B_MOD, NULL)); + + rc = test_foo(g_object_new(TEST_TYPE_A, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + rc = test_foo(g_object_new(TEST_TYPE_B, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + return 0; +} +]]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) + +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \ + test-a.o test-b.o test-b-mod.o test-fooable.o main.o]) + +# Currently borked +AT_XFAIL_IF([:]) +AT_CHECK([./main], [0], [Test:A foo called +42 +Test:B foo called +54 +]) + +AT_CLEANUP + +dnl Test that a dynamic type can override the interface implementation of a +dnl dynamic parent type. +AT_SETUP([interface method override (dynamic) #2]) +AT_KEYWORDS([runtime])dnl + +TEST_FOOABLE_IFACE() +TEST_FOOABLE_IMPL_DYN([Test:A], [G:Object], + [puts("Test:A foo called"); return 42;]) +TEST_FOOABLE_IMPL_DYN([Test:B], [Test:A], + [puts("Test:B foo called"); return 54;]) + +AT_DATA([main.c], +[[#include +#include "test-fooable.h" +#include "test-a.h" +#include "test-a-mod.h" +#include "test-b.h" +#include "test-b-mod.h" + +int main(void) +{ + int rc; + + g_type_module_use(g_object_new(TEST_TYPE_A_MOD, NULL)); + g_type_module_use(g_object_new(TEST_TYPE_B_MOD, NULL)); + + rc = test_foo(g_object_new(TEST_TYPE_A, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + rc = test_foo(g_object_new(TEST_TYPE_B, NULL)); + printf("%d\n", rc); + if (rc < 0) + return EXIT_FAILURE; + + return 0; +} +]]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) + +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \ + test-a.o test-a-mod.o test-b.o test-b-mod.o test-fooable.o main.o]) + +# Currently borked +AT_XFAIL_IF([:]) +AT_CHECK([./main], [0], [Test:A foo called +42 +Test:B foo called +54 +]) + +AT_CLEANUP diff --git a/testsuite.at b/testsuite.at index fccacb4..52dbd98 100644 --- a/testsuite.at +++ b/testsuite.at @@ -1,4 +1,4 @@ -AT_COPYRIGHT([Copyright © 2019 Nick Bowler +AT_COPYRIGHT([Copyright © 2019-2020 Nick Bowler License GPLv2+: GNU General Public License version 2 or any later version. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.]) @@ -30,3 +30,4 @@ m4_divert_push([PREPARE_TESTS])dnl m4_divert_pop([PREPARE_TESTS]) m4_include([tests/general.at]) +m4_include([tests/interface.at])