nkeynes@185 | 1 | /**
|
nkeynes@561 | 2 | * $Id$
|
nkeynes@185 | 3 | *
|
nkeynes@185 | 4 | * Test data loader.
|
nkeynes@185 | 5 | *
|
nkeynes@185 | 6 | * Copyright (c) 2006 Nathan Keynes.
|
nkeynes@185 | 7 | *
|
nkeynes@185 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@185 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@185 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@185 | 11 | * (at your option) any later version.
|
nkeynes@185 | 12 | *
|
nkeynes@185 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@185 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@185 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@185 | 16 | * GNU General Public License for more details.
|
nkeynes@185 | 17 | */
|
nkeynes@185 | 18 |
|
nkeynes@185 | 19 | #include <stdlib.h>
|
nkeynes@185 | 20 | #include <string.h>
|
nkeynes@185 | 21 | #include <ctype.h>
|
nkeynes@185 | 22 | #include <math.h>
|
nkeynes@185 | 23 | #include "testdata.h"
|
nkeynes@185 | 24 |
|
nkeynes@185 | 25 | #define DEFAULT_SIZE 1024
|
nkeynes@185 | 26 |
|
nkeynes@185 | 27 | /* get the next 32-byte aligned address that is no less than x */
|
nkeynes@185 | 28 | #define ALIGN_32(x) ((char *)((((unsigned int)(x))+0x1F)&0xFFFFFFE0))
|
nkeynes@185 | 29 |
|
nkeynes@185 | 30 | void free_test_dataset( test_data_t tests )
|
nkeynes@185 | 31 | {
|
nkeynes@185 | 32 | test_data_t next;
|
nkeynes@185 | 33 |
|
nkeynes@185 | 34 | do {
|
nkeynes@185 | 35 | next = tests->next;
|
nkeynes@185 | 36 | free(tests);
|
nkeynes@185 | 37 | tests = next;
|
nkeynes@185 | 38 | } while( next != NULL );
|
nkeynes@185 | 39 | }
|
nkeynes@185 | 40 |
|
nkeynes@185 | 41 | test_data_block_t get_test_data( test_data_t data, char *name )
|
nkeynes@185 | 42 | {
|
nkeynes@185 | 43 | int i;
|
nkeynes@185 | 44 | for( i=0; i<MAX_DATA_BLOCKS; i++ ) {
|
nkeynes@185 | 45 | if( data->item[i].name != NULL &&
|
nkeynes@185 | 46 | strcmp(name, data->item[i].name) == 0 ) {
|
nkeynes@185 | 47 | return &data->item[i];
|
nkeynes@185 | 48 | }
|
nkeynes@185 | 49 | }
|
nkeynes@185 | 50 | return NULL;
|
nkeynes@185 | 51 | }
|
nkeynes@185 | 52 |
|
nkeynes@185 | 53 | void dump_test_dataset( FILE *f, test_data_t dataset )
|
nkeynes@185 | 54 | {
|
nkeynes@185 | 55 | test_data_t test = dataset;
|
nkeynes@185 | 56 | int i;
|
nkeynes@185 | 57 | while( test != NULL ) {
|
nkeynes@185 | 58 | fprintf( f, "Test: %s\n", test->test_name );
|
nkeynes@185 | 59 | for( i=0; i<MAX_DATA_BLOCKS; i++ ) {
|
nkeynes@185 | 60 | if( test->item[i].name != NULL ) {
|
nkeynes@185 | 61 | fprintf( f, "Block: %s, %d bytes\n", test->item[i].name, test->item[i].length );
|
nkeynes@185 | 62 | fwrite_dump( f, test->item[i].data, test->item[i].length );
|
nkeynes@185 | 63 | }
|
nkeynes@185 | 64 | }
|
nkeynes@185 | 65 | test = test->next;
|
nkeynes@185 | 66 | }
|
nkeynes@185 | 67 | }
|
nkeynes@185 | 68 |
|
nkeynes@185 | 69 | int test_block_compare( test_data_block_t block, char *result, int result_length )
|
nkeynes@185 | 70 | {
|
nkeynes@185 | 71 | if( block->length != result_length )
|
nkeynes@185 | 72 | return -1;
|
nkeynes@185 | 73 | return memcmp( block->data, result, block->length );
|
nkeynes@185 | 74 | }
|
nkeynes@185 | 75 |
|
nkeynes@185 | 76 |
|
nkeynes@185 | 77 | /**
|
nkeynes@185 | 78 | * Load a batch of test data from the given IO stream.
|
nkeynes@185 | 79 | */
|
nkeynes@185 | 80 | test_data_t load_test_dataset( FILE *f )
|
nkeynes@185 | 81 | {
|
nkeynes@185 | 82 | test_data_t head = NULL;
|
nkeynes@185 | 83 | test_data_t current = NULL;
|
nkeynes@185 | 84 | test_data_t last = NULL;
|
nkeynes@185 | 85 | int current_size = 0;
|
nkeynes@185 | 86 | int current_block = -1;
|
nkeynes@185 | 87 | char *current_end = NULL;
|
nkeynes@185 | 88 | char *dataptr = NULL;
|
nkeynes@185 | 89 |
|
nkeynes@185 | 90 | char buf[512];
|
nkeynes@185 | 91 | char *line;
|
nkeynes@185 | 92 | while( fgets(buf, sizeof(buf), f ) != NULL ) {
|
nkeynes@185 | 93 | line = buf;
|
nkeynes@185 | 94 | while( isspace(*line) ) /* Trim leading whitespace */
|
nkeynes@185 | 95 | line++;
|
nkeynes@185 | 96 | if( line[0] == '[' ) { /* New test */
|
nkeynes@185 | 97 | char *test_name = line+1;
|
nkeynes@185 | 98 | char *end = strchr(test_name, ']');
|
nkeynes@185 | 99 | if( end != NULL )
|
nkeynes@185 | 100 | *end = '\0';
|
nkeynes@185 | 101 | current_size = DEFAULT_SIZE;
|
nkeynes@190 | 102 | test_data_t test = malloc(current_size);
|
nkeynes@345 | 103 | if( test == NULL ) {
|
nkeynes@345 | 104 | fprintf( stderr, "Memory alloc failed: %d\n", current_size );
|
nkeynes@345 | 105 | return NULL;
|
nkeynes@345 | 106 | }
|
nkeynes@190 | 107 | memset( test, 0, current_size );
|
nkeynes@185 | 108 |
|
nkeynes@185 | 109 | dataptr = (char *)(test+1);
|
nkeynes@185 | 110 | test->next = NULL;
|
nkeynes@185 | 111 | if( head == NULL )
|
nkeynes@185 | 112 | head = test;
|
nkeynes@185 | 113 | if( current != NULL )
|
nkeynes@185 | 114 | current->next = test;
|
nkeynes@185 | 115 | last = current;
|
nkeynes@185 | 116 | current = test;
|
nkeynes@185 | 117 | current_end = ((char *)test) + current_size;
|
nkeynes@185 | 118 | current_block = -1;
|
nkeynes@185 | 119 | strcpy( dataptr, test_name );
|
nkeynes@185 | 120 | test->test_name = dataptr;
|
nkeynes@185 | 121 | dataptr = ALIGN_32(dataptr + strlen(test_name)+1);
|
nkeynes@185 | 122 | } else if( *line == '#' ) { /* Comment */
|
nkeynes@185 | 123 | } else {
|
nkeynes@185 | 124 | char *equals = strrchr( line, '=' );
|
nkeynes@185 | 125 | if( equals != NULL ) {
|
nkeynes@185 | 126 | char *block_name = line;
|
nkeynes@185 | 127 | int len;
|
nkeynes@190 | 128 | char *p = equals;
|
nkeynes@190 | 129 | *p-- = '\0';
|
nkeynes@190 | 130 | while( isspace(*p) )
|
nkeynes@190 | 131 | *p-- = '\0';
|
nkeynes@185 | 132 | len = strlen(line)+1;
|
nkeynes@185 | 133 | if( dataptr + len > current_end ) {
|
nkeynes@185 | 134 | current_end += current_size;
|
nkeynes@185 | 135 | current_size *= 2;
|
nkeynes@185 | 136 | current = realloc(current, current_size );
|
nkeynes@345 | 137 | if( current == NULL ) {
|
nkeynes@345 | 138 | fprintf( stderr, "Memory alloc failed: %d\n", current_size );
|
nkeynes@345 | 139 | return NULL;
|
nkeynes@345 | 140 | }
|
nkeynes@185 | 141 | if( last != NULL )
|
nkeynes@185 | 142 | last->next = current;
|
nkeynes@185 | 143 | }
|
nkeynes@185 | 144 | current_block++;
|
nkeynes@185 | 145 | strcpy( dataptr, block_name );
|
nkeynes@185 | 146 | current->item[current_block].name = dataptr;
|
nkeynes@185 | 147 | dataptr = ALIGN_32(dataptr+len);
|
nkeynes@185 | 148 | current->item[current_block].data = dataptr;
|
nkeynes@185 | 149 | current->item[current_block].length = 0;
|
nkeynes@190 | 150 |
|
nkeynes@190 | 151 | line = equals+1;
|
nkeynes@190 | 152 | while( isspace(*line) )
|
nkeynes@190 | 153 | line++;
|
nkeynes@190 | 154 | }
|
nkeynes@190 | 155 |
|
nkeynes@190 | 156 | /* Data */
|
nkeynes@190 | 157 | if( current == NULL || current_block == -1 )
|
nkeynes@190 | 158 | continue;
|
nkeynes@190 | 159 | char *p = strtok(line, "\t\r\n ");
|
nkeynes@190 | 160 | while( p != NULL ) {
|
nkeynes@190 | 161 | if( dataptr + 8 > current_end ) {
|
nkeynes@190 | 162 | int old_size = current_size;
|
nkeynes@190 | 163 | current_end += current_size;
|
nkeynes@190 | 164 | current_size *= 2;
|
nkeynes@190 | 165 | current = realloc(current, current_size );
|
nkeynes@345 | 166 | if( current == NULL ) {
|
nkeynes@345 | 167 | fprintf( stderr, "Memory alloc failed: %d\n", current_size );
|
nkeynes@345 | 168 | return NULL;
|
nkeynes@345 | 169 | }
|
nkeynes@190 | 170 | memset( current + old_size, 0, old_size );
|
nkeynes@190 | 171 | if( last != NULL )
|
nkeynes@190 | 172 | last->next = current;
|
nkeynes@190 | 173 | }
|
nkeynes@190 | 174 | int len = strlen(p);
|
nkeynes@190 | 175 | int datalen = 0;
|
nkeynes@190 | 176 | char *dot = strchr(p, '.');
|
nkeynes@190 | 177 | if( dot != NULL ) { /* FP */
|
nkeynes@190 | 178 | if( p[len-1] == 'L' ) { /* Ending in L */
|
nkeynes@190 | 179 | p[len-1] = '\0';
|
nkeynes@190 | 180 | double d = strtod(p, NULL);
|
nkeynes@190 | 181 | *((double *)dataptr) = d;
|
nkeynes@190 | 182 | datalen = 8;
|
nkeynes@190 | 183 | } else {
|
nkeynes@190 | 184 | float f = (float)strtod(p,NULL);
|
nkeynes@190 | 185 | *((float *)dataptr) = f;
|
nkeynes@190 | 186 | datalen = 4;
|
nkeynes@185 | 187 | }
|
nkeynes@190 | 188 | } else {
|
nkeynes@190 | 189 | unsigned long value = strtoul(p, NULL, 16);
|
nkeynes@190 | 190 | if( len == 8 ) {
|
nkeynes@190 | 191 | *((unsigned int *)dataptr) = value;
|
nkeynes@190 | 192 | datalen = 4;
|
nkeynes@190 | 193 | } else if( len == 4 ) {
|
nkeynes@190 | 194 | *((unsigned short *)dataptr) = value;
|
nkeynes@190 | 195 | datalen = 2;
|
nkeynes@190 | 196 | } else if( len == 2 ) {
|
nkeynes@190 | 197 | *((unsigned char *)dataptr) = value;
|
nkeynes@190 | 198 | datalen = 1;
|
nkeynes@185 | 199 | }
|
nkeynes@185 | 200 | }
|
nkeynes@190 | 201 | dataptr += datalen;
|
nkeynes@190 | 202 | current->item[current_block].length += datalen;
|
nkeynes@190 | 203 | p = strtok(NULL, "\t\r\n ");
|
nkeynes@185 | 204 | }
|
nkeynes@185 | 205 | }
|
nkeynes@185 | 206 | }
|
nkeynes@185 | 207 | fclose(f);
|
nkeynes@185 | 208 | return head;
|
nkeynes@185 | 209 | }
|
nkeynes@263 | 210 |
|
nkeynes@263 | 211 | int run_tests( test_func_t *test_fns ) {
|
nkeynes@263 | 212 | int test_count, test_failures = 0;
|
nkeynes@263 | 213 |
|
nkeynes@263 | 214 | for( test_count=0; test_fns[test_count] != NULL; test_count++ ) {
|
nkeynes@263 | 215 | if( test_fns[test_count]() != 0 ) {
|
nkeynes@263 | 216 | fprintf( stderr, "Test %d failed\n", test_count+1 );
|
nkeynes@263 | 217 | test_failures++;
|
nkeynes@263 | 218 | }
|
nkeynes@263 | 219 | }
|
nkeynes@263 | 220 |
|
nkeynes@263 | 221 | /* report */
|
nkeynes@263 | 222 | fprintf( stderr, "%d/%d tests passed!\n", test_count - test_failures, test_count );
|
nkeynes@263 | 223 | return test_failures;
|
nkeynes@263 | 224 | }
|