| /* |
| * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. |
| * |
| * 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. |
| * |
| * You can also choose to distribute this program under the terms of |
| * the Unmodified Binary Distribution Licence (as given in the file |
| * COPYING.UBDL), provided that you have satisfied its requirements. |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); |
| |
| /** @file |
| * |
| * setjmp()/longjmp() tests |
| * |
| */ |
| |
| /* Forcibly enable assertions */ |
| #undef NDEBUG |
| |
| #include <stddef.h> |
| #include <assert.h> |
| #include <setjmp.h> |
| #include <ipxe/test.h> |
| |
| /** A setjmp()/longjmp() test */ |
| struct setjmp_test { |
| /** Jump buffer */ |
| jmp_buf env; |
| /** Expected value */ |
| int expected; |
| /** Test code file */ |
| const char *file; |
| /** Test code line */ |
| unsigned int line; |
| }; |
| |
| /** Expected jump */ |
| static struct setjmp_test *jumped; |
| |
| /** |
| * Report a setjmp() test result |
| * |
| * @v test setjmp()/longjmp() test |
| * |
| * This has to be implemented as a macro since if it were a function |
| * then the context saved by setjmp() would be invalidated when the |
| * function returned. |
| */ |
| #define setjmp_ok( test ) do { \ |
| int value; \ |
| /* Sanity check */ \ |
| assert ( jumped == NULL ); \ |
| /* Initialise test */ \ |
| (test)->expected = 0; \ |
| (test)->file = __FILE__; \ |
| (test)->line = __LINE__; \ |
| /* Perform setjmp() */ \ |
| value = setjmp ( (test)->env ); \ |
| /* Report setjmp()/longjmp() result */ \ |
| setjmp_return_ok ( (test), value ); \ |
| } while ( 0 ) |
| |
| /** |
| * Report a setjmp()/longjmp() test result |
| * |
| * @v test setjmp()/longjmp() test |
| * @v value Value returned from setjmp() |
| * |
| * This function ends up reporting results from either setjmp() or |
| * longjmp() tests (since calls to longjmp() will return via the |
| * corresponding setjmp()). It therefore uses the test code file and |
| * line stored in the test structure, which will represent the line |
| * from which either setjmp() or longjmp() was called. |
| */ |
| static void setjmp_return_ok ( struct setjmp_test *test, int value ) { |
| |
| /* Determine whether this was reached via setjmp() or longjmp() */ |
| if ( value == 0 ) { |
| /* This is the initial call to setjmp() */ |
| okx ( test->expected == 0, test->file, test->line ); |
| okx ( jumped == NULL, test->file, test->line ); |
| } else { |
| /* This is reached via a call to longjmp() */ |
| okx ( value == test->expected, test->file, test->line ); |
| okx ( jumped == test, test->file, test->line ); |
| } |
| |
| /* Clear expected jump */ |
| jumped = NULL; |
| } |
| |
| /** |
| * Report a longjmp() test result |
| * |
| * @v test setjmp()/longjmp() test |
| * @v file Test code file |
| * @v line Test code line |
| */ |
| static void __attribute__ (( noreturn )) |
| longjmp_okx ( struct setjmp_test *test, int value, |
| const char *file, unsigned int line ) { |
| |
| /* Record expected value. A zero passed to longjmp() should |
| * result in setjmp() returning a value of one. |
| */ |
| test->expected = ( value ? value : 1 ); |
| |
| /* Record test code file and line */ |
| test->file = file; |
| test->line = line; |
| |
| /* Record expected jump */ |
| jumped = test; |
| |
| /* Perform longjmp(). Should return via setjmp_okx() */ |
| longjmp ( test->env, value ); |
| |
| /* longjmp() should never return */ |
| assert ( 0 ); |
| } |
| #define longjmp_ok( test, value ) \ |
| longjmp_okx ( test, value, __FILE__, __LINE__ ) |
| |
| /** |
| * Perform setjmp()/longjmp() self-tests |
| * |
| */ |
| static void setjmp_test_exec ( void ) { |
| static struct setjmp_test alpha; |
| static struct setjmp_test beta; |
| static int iteration; |
| |
| /* This is one of the very few situations in which the |
| * "for-case" pattern is justified. |
| */ |
| for ( iteration = 0 ; iteration < 10 ; iteration++ ) { |
| DBGC ( jumped, "SETJMP test iteration %d\n", iteration ); |
| switch ( iteration ) { |
| case 0: setjmp_ok ( &alpha ); break; |
| case 1: setjmp_ok ( &beta ); break; |
| case 2: longjmp_ok ( &alpha, 0 ); |
| case 3: longjmp_ok ( &alpha, 1 ); |
| case 4: longjmp_ok ( &alpha, 2 ); |
| case 5: longjmp_ok ( &beta, 17 ); |
| case 6: longjmp_ok ( &beta, 29 ); |
| case 7: longjmp_ok ( &alpha, -1 ); |
| case 8: longjmp_ok ( &beta, 0 ); |
| case 9: longjmp_ok ( &beta, 42 ); |
| } |
| } |
| } |
| |
| /** setjmp()/longjmp() self-test */ |
| struct self_test setjmp_test __self_test = { |
| .name = "setjmp", |
| .exec = setjmp_test_exec, |
| }; |