| /* | |
| * 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 | |
| * | |
| * Line buffer self-tests | |
| * | |
| */ | |
| /* Forcibly enable assertions */ | |
| #undef NDEBUG | |
| #include <string.h> | |
| #include <assert.h> | |
| #include <ipxe/linebuf.h> | |
| #include <ipxe/test.h> | |
| /** Define inline raw data */ | |
| #define DATA(...) { __VA_ARGS__ } | |
| /** Define inline lines */ | |
| #define LINES(...) { __VA_ARGS__ } | |
| /** A line buffer test */ | |
| struct linebuf_test { | |
| /** Raw data */ | |
| const void *data; | |
| /** Length of raw data */ | |
| size_t len; | |
| /** Expected sequence of lines */ | |
| const char **lines; | |
| /** Number of expected lines */ | |
| unsigned int count; | |
| }; | |
| /** Line buffer test expected failure indicator */ | |
| static const char linebuf_failure[1]; | |
| /** | |
| * Define a line buffer test | |
| * | |
| * @v name Test name | |
| * @v DATA Raw data | |
| * @v LINES Expected sequence of lines | |
| * @ret test Line buffer test | |
| */ | |
| #define LINEBUF_TEST( name, DATA, LINES ) \ | |
| static const char name ## _data[] = DATA; \ | |
| static const char * name ## _lines[] = LINES; \ | |
| static struct linebuf_test name = { \ | |
| .data = name ## _data, \ | |
| .len = ( sizeof ( name ## _data ) - 1 /* NUL */ ), \ | |
| .lines = name ## _lines, \ | |
| .count = ( sizeof ( name ## _lines ) / \ | |
| sizeof ( name ## _lines[0] ) ), \ | |
| } | |
| /** Simple line buffer test */ | |
| LINEBUF_TEST ( simple, | |
| ( "HTTP/1.1 200 OK\r\n" | |
| "Content-Length: 123\r\n" | |
| "Content-Type: text/plain\r\n" | |
| "\r\n" ), | |
| LINES ( "HTTP/1.1 200 OK", | |
| "Content-Length: 123", | |
| "Content-Type: text/plain", | |
| "" ) ); | |
| /** Mixed line terminators */ | |
| LINEBUF_TEST ( mixed, | |
| ( "LF only\n" "CRLF\r\n" "\n" "\n" "\r\n" "\r\n" "CR only\r" ), | |
| LINES ( "LF only", "CRLF", "", "", "", "", | |
| NULL /* \r should not be treated as a terminator */ ) ); | |
| /** Split consumption: part 1 */ | |
| LINEBUF_TEST ( split_1, | |
| ( "This line was" ), | |
| LINES ( NULL ) ); | |
| /** Split consumption: part 2 */ | |
| LINEBUF_TEST ( split_2, | |
| ( " split across" ), | |
| LINES ( NULL ) ); | |
| /** Split consumption: part 3 */ | |
| LINEBUF_TEST ( split_3, | |
| ( " multiple calls\r\nand so was this one\r" ), | |
| LINES ( "This line was split across multiple calls", NULL ) ); | |
| /** Split consumption: part 4 */ | |
| LINEBUF_TEST ( split_4, | |
| ( "\nbut not this one\r\n" ), | |
| LINES ( "and so was this one", "but not this one" ) ); | |
| /** Split consumption: part 5 */ | |
| LINEBUF_TEST ( split_5, | |
| ( "" ), | |
| LINES ( NULL ) ); | |
| /** Split consumption: part 6 */ | |
| LINEBUF_TEST ( split_6, | |
| ( "This line came after a zero-length call\r\n" ), | |
| LINES ( "This line came after a zero-length call" ) ); | |
| /** Embedded NULs */ | |
| LINEBUF_TEST ( embedded_nuls, | |
| ( "This\r\ntest\r\nincludes\r\n\r\nsome\0binary\0data\r\n" ), | |
| LINES ( "This", "test", "includes", "", linebuf_failure ) ); | |
| /** | |
| * Report line buffer initialisation test result | |
| * | |
| * @v linebuf Line buffer | |
| * @v file Test code file | |
| * @v line Test code line | |
| */ | |
| static void linebuf_init_okx ( struct line_buffer *linebuf, | |
| const char *file, unsigned int line ) { | |
| /* Initialise line buffer */ | |
| memset ( linebuf, 0, sizeof ( *linebuf ) ); | |
| okx ( buffered_line ( linebuf ) == NULL, file, line ); | |
| } | |
| #define linebuf_init_ok( linebuf ) \ | |
| linebuf_init_okx ( linebuf, __FILE__, __LINE__ ) | |
| /** | |
| * Report line buffer consumption test result | |
| * | |
| * @v test Line buffer test | |
| * @v linebuf Line buffer | |
| * @v file Test code file | |
| * @v line Test code line | |
| */ | |
| static void linebuf_consume_okx ( struct linebuf_test *test, | |
| struct line_buffer *linebuf, | |
| const char *file, unsigned int line ) { | |
| const char *data = test->data; | |
| size_t remaining = test->len; | |
| int len; | |
| unsigned int i; | |
| const char *expected; | |
| char *actual; | |
| int rc; | |
| DBGC ( test, "LINEBUF %p:\n", test ); | |
| DBGC_HDA ( test, 0, data, remaining ); | |
| /* Consume data one line at a time */ | |
| for ( i = 0 ; i < test->count ; i++ ) { | |
| /* Add data to line buffer */ | |
| len = line_buffer ( linebuf, data, remaining ); | |
| /* Get buffered line, if any */ | |
| actual = buffered_line ( linebuf ); | |
| if ( len < 0 ) { | |
| rc = len; | |
| DBGC ( test, "LINEBUF %p %s\n", test, strerror ( rc ) ); | |
| } else if ( actual != NULL ) { | |
| DBGC ( test, "LINEBUF %p \"%s\" (consumed %d)\n", | |
| test, actual, len ); | |
| } else { | |
| DBGC ( test, "LINEBUF %p unterminated (consumed %d)\n", | |
| test, len ); | |
| } | |
| /* Check for success/failure */ | |
| expected = test->lines[i]; | |
| if ( expected == linebuf_failure ) { | |
| rc = len; | |
| okx ( rc < 0, file, line ); | |
| okx ( remaining > 0, file, line ); | |
| return; | |
| } | |
| okx ( len >= 0, file, line ); | |
| okx ( ( ( size_t ) len ) <= remaining, file, line ); | |
| /* Check expected result */ | |
| if ( expected == NULL ) { | |
| okx ( actual == NULL, file, line ); | |
| } else { | |
| okx ( actual != NULL, file, line ); | |
| okx ( strcmp ( actual, expected ) == 0, file, line ); | |
| } | |
| /* Consume data */ | |
| data += len; | |
| remaining -= len; | |
| } | |
| /* Check that all data was consumed */ | |
| okx ( remaining == 0, file, line ); | |
| } | |
| #define linebuf_consume_ok( test, linebuf ) \ | |
| linebuf_consume_okx ( test, linebuf, __FILE__, __LINE__ ) | |
| /** | |
| * Report line buffer accumulation test result | |
| * | |
| * @v test Line buffer test | |
| * @v linebuf Line buffer | |
| * @v file Test code file | |
| * @v line Test code line | |
| */ | |
| static void linebuf_accumulated_okx ( struct linebuf_test *test, | |
| struct line_buffer *linebuf, | |
| const char *file, unsigned int line ) { | |
| const char *actual; | |
| const char *expected; | |
| unsigned int i; | |
| /* Check each accumulated line */ | |
| actual = linebuf->data; | |
| for ( i = 0 ; i < test->count ; i++ ) { | |
| /* Check accumulated line */ | |
| okx ( actual != NULL, file, line ); | |
| okx ( actual >= linebuf->data, file, line ); | |
| expected = test->lines[i]; | |
| if ( ( expected == NULL ) || ( expected == linebuf_failure ) ) | |
| return; | |
| okx ( strcmp ( actual, expected ) == 0, file, line ); | |
| /* Move to next line */ | |
| actual += ( strlen ( actual ) + 1 /* NUL */ ); | |
| okx ( actual <= ( linebuf->data + linebuf->len ), file, line ); | |
| } | |
| } | |
| #define linebuf_accumulated_ok( test, linebuf ) \ | |
| linebuf_accumulated_okx ( test, linebuf, __FILE__, __LINE__ ) | |
| /** | |
| * Report line buffer emptying test result | |
| * | |
| * @v linebuf Line buffer | |
| * @v file Test code file | |
| * @v line Test code line | |
| */ | |
| static void linebuf_empty_okx ( struct line_buffer *linebuf, | |
| const char *file, unsigned int line ) { | |
| /* Empty line buffer */ | |
| empty_line_buffer ( linebuf ); | |
| okx ( buffered_line ( linebuf ) == NULL, file, line ); | |
| } | |
| #define linebuf_empty_ok( linebuf ) \ | |
| linebuf_empty_okx ( linebuf, __FILE__, __LINE__ ) | |
| /** | |
| * Report line buffer combined test result | |
| * | |
| * @v test Line buffer test | |
| * @v file Test code file | |
| * @v line Test code line | |
| */ | |
| static void linebuf_okx ( struct linebuf_test *test, const char *file, | |
| unsigned int line ) { | |
| struct line_buffer linebuf; | |
| linebuf_init_okx ( &linebuf, file, line ); | |
| linebuf_consume_okx ( test, &linebuf, file, line ); | |
| linebuf_accumulated_okx ( test, &linebuf, file, line ); | |
| linebuf_empty_okx ( &linebuf, file, line ); | |
| } | |
| #define linebuf_ok( test ) \ | |
| linebuf_okx ( test, __FILE__, __LINE__ ) | |
| /** | |
| * Perform line buffer self-tests | |
| * | |
| */ | |
| static void linebuf_test_exec ( void ) { | |
| struct line_buffer linebuf; | |
| /* Basic tests */ | |
| linebuf_ok ( &simple ); | |
| linebuf_ok ( &mixed ); | |
| /* Split consumption test */ | |
| linebuf_init_ok ( &linebuf ); | |
| linebuf_consume_ok ( &split_1, &linebuf ); | |
| linebuf_consume_ok ( &split_2, &linebuf ); | |
| linebuf_consume_ok ( &split_3, &linebuf ); | |
| linebuf_consume_ok ( &split_4, &linebuf ); | |
| linebuf_consume_ok ( &split_5, &linebuf ); | |
| linebuf_consume_ok ( &split_6, &linebuf ); | |
| linebuf_empty_ok ( &linebuf ); | |
| /* Embedded NULs */ | |
| linebuf_ok ( &embedded_nuls ); | |
| } | |
| /** Line buffer self-test */ | |
| struct self_test linebuf_test __self_test = { | |
| .name = "linebuf", | |
| .exec = linebuf_test_exec, | |
| }; |