| # -*-perl-*- |
| $description = "Test the shared object load API."; |
| |
| $details = "Verify the different aspects of the shared object API."; |
| |
| # Don't do anything if this system doesn't support "load" |
| exists $FEATURES{load} or return -1; |
| |
| my $cc = get_config('CC'); |
| if (! $cc) { |
| $verbose and print "Skipping load test: no CC defined\n"; |
| return -1; |
| } |
| |
| # First build a shared object |
| # Provide both a default and non-default load symbol |
| |
| unlink(qw(testapi.c testapi.so)); |
| |
| open(my $F, '> testapi.c') or die "open: testapi.c: $!\n"; |
| print $F <<'EOF' ; |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "gnumake.h" |
| |
| char *getenv (const char*); |
| |
| int plugin_is_GPL_compatible; |
| |
| int testapi_gmk_setup (unsigned int abi, const gmk_floc *floc); |
| |
| static char * |
| test_eval (const char *buf) |
| { |
| gmk_eval (buf, 0); |
| return NULL; |
| } |
| |
| static char * |
| test_expand (const char *val) |
| { |
| return gmk_expand (val); |
| } |
| |
| static char * |
| test_noexpand (const char *val) |
| { |
| char *str = gmk_alloc (strlen (val) + 1); |
| strcpy (str, val); |
| return str; |
| } |
| |
| static char * |
| func_test (const char *funcname, unsigned int argc, char **argv) |
| { |
| char *mem; |
| |
| (void)argc; |
| |
| if (strcmp (funcname, "test-expand") == 0) |
| return test_expand (argv[0]); |
| |
| if (strcmp (funcname, "test-eval") == 0) |
| return test_eval (argv[0]); |
| |
| if (strcmp (funcname, "test-noexpand") == 0) |
| return test_noexpand (argv[0]); |
| |
| mem = gmk_alloc (sizeof ("unknown")); |
| strcpy (mem, "unknown"); |
| return mem; |
| } |
| |
| int |
| testapi_gmk_setup (unsigned int abi, const gmk_floc *floc) |
| { |
| const char *verbose = getenv ("TESTAPI_VERBOSE"); |
| |
| (void)abi; |
| |
| gmk_add_function ("test-expand", func_test, 1, 1, GMK_FUNC_DEFAULT); |
| gmk_add_function ("test-noexpand", func_test, 1, 1, GMK_FUNC_NOEXPAND); |
| gmk_add_function ("test-eval", func_test, 1, 1, GMK_FUNC_DEFAULT); |
| gmk_add_function ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.", func_test, 0, 0, 0); |
| |
| if (verbose) |
| { |
| printf ("testapi_gmk_setup\n"); |
| |
| if (verbose[0] == '2') |
| printf ("%s:%lu\n", floc->filenm, floc->lineno); |
| } |
| |
| if (getenv ("TESTAPI_KEEP")) |
| return -1; |
| |
| return 1; |
| } |
| |
| int |
| alternative_setup () |
| { |
| gmk_add_function ("test-expand", func_test, 1, 1, GMK_FUNC_DEFAULT); |
| gmk_add_function ("test-noexpand", func_test, 1, 1, GMK_FUNC_NOEXPAND); |
| gmk_add_function ("test-eval", func_test, 1, 1, GMK_FUNC_DEFAULT); |
| gmk_add_function ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.", func_test, 0, 0, 0); |
| |
| if (getenv ("TESTAPI_VERBOSE")) |
| printf ("alternative_setup\n"); |
| |
| if (getenv ("TESTAPI_KEEP")) |
| return -1; |
| |
| return 1; |
| } |
| |
| void |
| testapi_gmk_unload () |
| { |
| const char *s = getenv ("TESTAPI_VERBOSE"); |
| if (s && *s == '3') |
| printf ("testapi_gmk_unload\n"); |
| } |
| |
| EOF |
| close($F) or die "close: testapi.c: $!\n"; |
| |
| # Make sure we can compile |
| |
| my $cflags = get_config('CFLAGS'); |
| my $cppflags = get_config('CPPFLAGS'); |
| my $ldflags = get_config('LDFLAGS'); |
| my $sobuild = "\"$cc\" ".($srcdir? "-I$srcdir/src":'')." $cppflags $cflags -shared -fPIC $ldflags -o testapi.so testapi.c"; |
| |
| my $clog = `$sobuild 2>&1`; |
| if ($? != 0) { |
| $verbose and print "Failed to build testapi.so:\n$sobuild\n$_"; |
| return -1; |
| } |
| |
| # TEST 1 |
| # Check the gmk_expand() function |
| run_make_test(q! |
| EXPAND = expansion |
| all: ; @echo $(test-expand $$(EXPAND)) |
| load testapi.so |
| !, |
| '', "expansion\n"); |
| |
| # TEST 2 |
| # Check the eval operation. Prove that the argument is expanded only once |
| run_make_test(q! |
| load testapi.so |
| TEST = bye |
| ASSIGN = VAR = $(TEST) $(shell echo there) |
| $(test-eval $(value ASSIGN)) |
| TEST = hi |
| all:;@echo '$(VAR)' |
| !, |
| '', "hi there\n"); |
| |
| # TEST 2 |
| # Check the no-expand capability |
| run_make_test(q! |
| load testapi.so |
| TEST = hi |
| all:;@echo '$(test-noexpand $(TEST))' |
| !, |
| '', "\$(TEST)\n"); |
| |
| |
| # During all subsequent tests testapi.so exists. |
| # |
| my @loads = ('', q! |
| load testapi.so |
| load testapi.so |
| -load testapi.so |
| -load testapi.so |
| $(eval load testapi.so) |
| $(eval -load testapi.so) |
| !); |
| |
| for my $extra_loads (@loads) { |
| my $n = 5; |
| if ($extra_loads) { |
| $n = 12; |
| } |
| # sv 63045. |
| # Test that having unloaded a shared object make loads it again, even if the |
| # shared object is not updated. |
| $ENV{TESTAPI_VERBOSE} = 1; |
| run_make_test(" |
| load testapi.so |
| $extra_loads |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: force; \$(info \$@) |
| force:; |
| .PHONY: force |
| ", '', "testapi_gmk_setup\ntestapi.so\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); |
| |
| # sv 63045. |
| # Same as above, but testapi_gmk_setup returned -1. |
| $ENV{TESTAPI_KEEP} = 1; |
| $ENV{TESTAPI_VERBOSE} = 1; |
| run_make_test(" |
| load testapi.so |
| $extra_loads |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: force; \$(info \$@) |
| force:; |
| .PHONY: force |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); |
| |
| # sv 63045. |
| # Test that make exits, unless make can successfully update an unloaded shared |
| # object. |
| $ENV{TESTAPI_VERBOSE} = 1; |
| run_make_test(" |
| load testapi.so |
| $extra_loads |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: force; @#HELPER# fail 1 |
| force:; |
| .PHONY: force |
| ", '', "testapi_gmk_setup\nfail 1\n#MAKE#: *** [#MAKEFILE#:$n: testapi.so] Error 1\n", 512); |
| |
| # sv 63045. |
| # Same as above, but testapi_gmk_setup returned -1. |
| $ENV{TESTAPI_KEEP} = 1; |
| $ENV{TESTAPI_VERBOSE} = 1; |
| run_make_test(" |
| load testapi.so |
| $extra_loads |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: force; @#HELPER# fail 1 |
| force:; |
| .PHONY: force |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\n"); |
| |
| # sv 63100. |
| # Test that make supplies the correct floc when the shared object is loaded |
| # again. |
| $ENV{TESTAPI_VERBOSE} = 2; |
| run_make_test(" |
| load testapi.so |
| $extra_loads |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: force; \$(info \$@) |
| force:; |
| .PHONY: force |
| ", '', "testapi_gmk_setup\n#MAKEFILE#:2\ntestapi.so\ntestapi_gmk_setup\n#MAKEFILE#:2\nhello\n#MAKE#: 'all' is up to date.\n"); |
| } |
| |
| my @names = ('testapi.so', './testapi.so', '#PWD#/testapi.so'); |
| |
| for my $name (@names) { |
| |
| # Test the make correctly figures out the name of the close function and runs |
| # the close function. |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(" |
| load $name |
| all:; \$(info \$(test-expand hello)) |
| ", '', "testapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); |
| } |
| |
| # Same as above, but the setup function is custom. |
| @names = ('testapi.so(alternative_setup)', './testapi.so(alternative_setup)', |
| '#PWD#/testapi.so(alternative_setup)'); |
| for my $name (@names) { |
| |
| # Test the close function. |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(" |
| load $name |
| all:; \$(info \$(test-expand hello)) |
| ", '', "alternative_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); |
| } |
| |
| # Test that makes runs the close function on failure. |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(q! |
| load testapi.so |
| all: bad_preqreq; : |
| !, '', "testapi_gmk_setup\n#MAKE#: *** No rule to make target 'bad_preqreq', needed by 'all'. Stop.\ntestapi_gmk_unload\n", 512); |
| |
| # Test that make unloads a shared object, calls the close function, loads |
| # the plugin again, and then calls the close function again on exit. |
| &utouch(-10, 'testapi.so'); |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(" |
| load testapi.so |
| all:; \$(info \$(test-expand hello)) |
| testapi.so: testapi.c; $sobuild |
| ", '', "testapi_gmk_setup\ntestapi_gmk_unload\n$sobuild\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload"); |
| |
| # Test that make unloads a shared object, calls the close function, loads |
| # the plugin again, and then calls the close function again on exit. |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(q! |
| load testapi.so |
| all:; $(info $(test-expand hello)) |
| testapi.so: force; $(info $@) |
| force:; |
| .PHONY: force |
| !, '', "testapi_gmk_setup\ntestapi_gmk_unload\ntestapi.so\ntestapi_gmk_setup\nhello\n#MAKE#: 'all' is up to date.\ntestapi_gmk_unload\n"); |
| |
| unlink(qw(testapi.c testapi.so)) unless $keep; |
| |
| # Test that make does not run the close function, unless the shared object |
| # loaded successfully. |
| unlink('testapi.so'); |
| $ENV{TESTAPI_VERBOSE} = 3; |
| run_make_test(q! |
| load testapi.so |
| all:; : |
| !, '', "#MAKEFILE#:2: testapi.so: cannot open shared object file: $ERR_no_such_file\n#MAKEFILE#:2: *** testapi.so: failed to load. Stop.\n", 512); |
| |
| # This tells the test driver that the perl test script executed properly. |
| 1; |