| /* | |
| * 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, | |
| }; |