dnl Copyright © 2020, 2022-2023 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 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], TEST_CLASSNAME_REPLACE_SEP([$1], [-]), [$2], TEST_CLASSNAME_REPLACE_SEP([$2], [-]), [$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], [[%ctop{ #include %} %{ #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) onerror 88 { $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 #include "test-fooable.h" #include "test-a.h" int main(void) { int rc; g_type_init(); 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]) TEST_LINK_GOBJECT([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 #include "test-fooable.h" #include "test-a.h" #include "test-a-mod.h" int main(void) { int rc; g_type_init(); 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]) TEST_LINK_GOBJECT([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 #include "test-fooable.h" #include "test-a.h" #include "test-b.h" int main(void) { int rc; g_type_init(); 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]) TEST_LINK_GOBJECT([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 #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_init(); 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]) TEST_LINK_GOBJECT([main], [test-a.o test-b.o test-b-mod.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 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 #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_init(); 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]) TEST_LINK_GOBJECT([main], [test-a.o test-a-mod.o test-b.o test-b-mod.o test-fooable.o main.o]) AT_CHECK([./main], [0], [Test:A foo called 42 Test:B foo called 54 ]) AT_CLEANUP AT_SETUP([interface method chaining]) AT_KEYWORDS([runtime])dnl TEST_FOOABLE_IFACE() TEST_FOOABLE_IMPL([Test:A], [G:Object], [puts("Test:A foo called"); return PARENT_HANDLER(go);]) TEST_FOOABLE_IMPL([Test:B], [Test:A], [puts("Test:B foo called"); return PARENT_HANDLER(go);]) TEST_FOOABLE_IMPL([Test:C], [Test:B], [puts("Test:C foo called"); return PARENT_HANDLER(go);]) AT_DATA([main.c], [[#include #include #include #include "test-fooable.h" #include "test-a.h" #include "test-b.h" #include "test-c.h" int main(void) { int rc; g_type_init(); rc = test_foo(g_object_new(TEST_TYPE_C, NULL)); printf("%d\n", rc); if (rc < 0) return EXIT_FAILURE; return 0; } ]]) TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) TEST_LINK_GOBJECT([main], [test-a.o test-b.o test-c.o test-fooable.o main.o]) AT_CHECK([./main], [0], [Test:C foo called Test:B foo called Test:A foo called 88 ]) AT_CLEANUP AT_SETUP([interface method chaining (dynamic)]) AT_KEYWORDS([runtime])dnl TEST_FOOABLE_IFACE() TEST_FOOABLE_IMPL_DYN([Test:A], [G:Object], [puts("Test:A foo called"); return PARENT_HANDLER(go);]) TEST_FOOABLE_IMPL_DYN([Test:B], [Test:A], [puts("Test:B foo called"); return PARENT_HANDLER(go);]) TEST_FOOABLE_IMPL_DYN([Test:C], [Test:B], [puts("Test:C foo called"); return PARENT_HANDLER(go);]) AT_DATA([main.c], [[#include #include #include #include "test-fooable.h" #include "test-a.h" #include "test-a-mod.h" #include "test-b.h" #include "test-b-mod.h" #include "test-c.h" #include "test-c-mod.h" int main(void) { int rc; g_type_init(); g_type_module_use(g_object_new(TEST_TYPE_A_MOD, NULL)); g_type_module_use(g_object_new(TEST_TYPE_B_MOD, NULL)); g_type_module_use(g_object_new(TEST_TYPE_C_MOD, NULL)); rc = test_foo(g_object_new(TEST_TYPE_C, NULL)); printf("%d\n", rc); if (rc < 0) return EXIT_FAILURE; return 0; } ]]) TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) TEST_LINK_GOBJECT([main], m4_join([ ], [test-a.o test-a-mod.o test-b.o test-b-mod.o test-c.o test-c-mod.o], [test-fooable.o main.o])) AT_CHECK([./main], [0], [Test:C foo called Test:B foo called Test:A foo called 88 ]) AT_CLEANUP