1/* 2** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com> 3** 4** This program is free software; you can redistribute it and/or modify 5** it under the terms of the GNU Lesser General Public License as published by 6** the Free Software Foundation; either version 2.1 of the License, or 7** (at your option) any later version. 8** 9** This program is distributed in the hope that it will be useful, 10** but WITHOUT ANY WARRANTY; without even the implied warranty of 11** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12** GNU Lesser General Public License for more details. 13** 14** You should have received a copy of the GNU Lesser General Public License 15** along with this program; if not, write to the Free Software 16** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17*/ 18 19#include "sfconfig.h" 20 21#include <stdio.h> 22#include <stdlib.h> 23 24#if HAVE_UNISTD_H 25#include <unistd.h> 26#else 27#include "sf_unistd.h" 28#endif 29 30#include <string.h> 31#include <errno.h> 32#include <inttypes.h> 33 34#include "common.h" 35 36#include "test_main.h" 37 38static void make_data (int *data, int len, int seed) ; 39 40static void file_open_test (const char *filename) ; 41static void file_read_write_test (const char *filename) ; 42static void file_truncate_test (const char *filename) ; 43 44static void test_open_or_die (SF_PRIVATE *psf, int linenum) ; 45static void test_close_or_die (SF_PRIVATE *psf, int linenum) ; 46 47static void test_write_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ; 48static void test_read_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ; 49static void test_equal_or_die (int *array1, int *array2, int len, int linenum) ; 50static void test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) ; 51static void test_tell_or_die (SF_PRIVATE *psf, sf_count_t expected_position, int linenum) ; 52 53 54 55/*============================================================================== 56** Actual test functions. 57*/ 58 59static void 60file_open_test (const char *filename) 61{ SF_PRIVATE sf_data, *psf ; 62 int error ; 63 64 print_test_name ("Testing file open") ; 65 66 memset (&sf_data, 0, sizeof (sf_data)) ; 67 psf = &sf_data ; 68 69 /* Ensure that the file doesn't already exist. */ 70 if (unlink (filename) != 0 && errno != ENOENT) 71 { printf ("\n\nLine %d: unlink failed (%d) : %s\n\n", __LINE__, errno, strerror (errno)) ; 72 exit (1) ; 73 } ; 74 75 psf->file.mode = SFM_READ ; 76 snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ; 77 78 /* Test that open for read fails if the file doesn't exist. */ 79 error = psf_fopen (psf) ; 80 if (error == 0) 81 { printf ("\n\nLine %d: psf_fopen() should have failed.\n\n", __LINE__) ; 82 exit (1) ; 83 } ; 84 85 /* Reset error to zero. */ 86 psf->error = SFE_NO_ERROR ; 87 88 /* Test file open in write mode. */ 89 psf->file.mode = SFM_WRITE ; 90 test_open_or_die (psf, __LINE__) ; 91 92 test_close_or_die (psf, __LINE__) ; 93 94 unlink (psf->file.path) ; 95 96 /* Test file open in read/write mode for a non-existant file. */ 97 psf->file.mode = SFM_RDWR ; 98 test_open_or_die (psf, __LINE__) ; 99 100 test_close_or_die (psf, __LINE__) ; 101 102 /* Test file open in read/write mode for an existing file. */ 103 psf->file.mode = SFM_RDWR ; 104 test_open_or_die (psf, __LINE__) ; 105 106 test_close_or_die (psf, __LINE__) ; 107 108 unlink (psf->file.path) ; 109 puts ("ok") ; 110} /* file_open_test */ 111 112static void 113file_read_write_test (const char *filename) 114{ static int data_out [512] ; 115 static int data_in [512] ; 116 117 SF_PRIVATE sf_data, *psf ; 118 sf_count_t retval ; 119 120 /* 121 ** Open a new file and write two blocks of data to the file. After each 122 ** write, test that psf_get_filelen() returns the new length. 123 */ 124 125 print_test_name ("Testing file write") ; 126 127 memset (&sf_data, 0, sizeof (sf_data)) ; 128 psf = &sf_data ; 129 snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ; 130 131 /* Test file open in write mode. */ 132 psf->file.mode = SFM_WRITE ; 133 test_open_or_die (psf, __LINE__) ; 134 135 make_data (data_out, ARRAY_LEN (data_out), 1) ; 136 test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), sizeof (data_out), __LINE__) ; 137 138 if ((retval = psf_get_filelen (psf)) != sizeof (data_out)) 139 { printf ("\n\nLine %d: file length after write is not correct (%" PRId64 " should be %zd).\n\n", __LINE__, retval, sizeof (data_out)) ; 140 if (retval == 0) 141 printf ("An fsync() may be necessary before fstat() in psf_get_filelen().\n\n") ; 142 exit (1) ; 143 } ; 144 145 make_data (data_out, ARRAY_LEN (data_out), 2) ; 146 test_write_or_die (psf, data_out, ARRAY_LEN (data_out), sizeof (data_out [0]), 2 * sizeof (data_out), __LINE__) ; 147 148 if ((retval = psf_get_filelen (psf)) != 2 * sizeof (data_out)) 149 { printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 2 * sizeof (data_out)) ; 150 exit (1) ; 151 } ; 152 153 test_close_or_die (psf, __LINE__) ; 154 puts ("ok") ; 155 156 /* 157 ** Now open the file in read mode, check the file length and check 158 ** that the data is correct. 159 */ 160 161 print_test_name ("Testing file read") ; 162 163 /* Test file open in write mode. */ 164 psf->file.mode = SFM_READ ; 165 test_open_or_die (psf, __LINE__) ; 166 167 make_data (data_out, ARRAY_LEN (data_out), 1) ; 168 test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; 169 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 170 171 make_data (data_out, ARRAY_LEN (data_out), 2) ; 172 test_read_or_die (psf, data_in, sizeof (data_in [0]), ARRAY_LEN (data_in), 2 * sizeof (data_in), __LINE__) ; 173 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 174 175 test_close_or_die (psf, __LINE__) ; 176 177 puts ("ok") ; 178 179 /* 180 ** Open the file in read/write mode, seek around a bit and then seek to 181 ** the end of the file and write another block of data (3rd block). Then 182 ** go back and check that all three blocks are correct. 183 */ 184 185 print_test_name ("Testing file seek") ; 186 187 /* Test file open in read/write mode. */ 188 psf->file.mode = SFM_RDWR ; 189 test_open_or_die (psf, __LINE__) ; 190 191 test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ; 192 test_seek_or_die (psf, 0, SEEK_END, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; 193 test_seek_or_die (psf, -1 * SIGNED_SIZEOF (data_out), SEEK_CUR, (sf_count_t) sizeof (data_out), __LINE__) ; 194 195 test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_CUR, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; 196 make_data (data_out, ARRAY_LEN (data_out), 3) ; 197 test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 3 * sizeof (data_out), __LINE__) ; 198 199 test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ; 200 make_data (data_out, ARRAY_LEN (data_out), 1) ; 201 test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; 202 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 203 204 test_seek_or_die (psf, 2 * SIGNED_SIZEOF (data_out), SEEK_SET, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; 205 make_data (data_out, ARRAY_LEN (data_out), 3) ; 206 test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ; 207 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 208 209 test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ; 210 make_data (data_out, ARRAY_LEN (data_out), 2) ; 211 test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ; 212 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 213 214 test_close_or_die (psf, __LINE__) ; 215 puts ("ok") ; 216 217 /* 218 ** Now test operations with a non-zero psf->fileoffset field. This field 219 ** sets an artificial file start positions so that a seek to the start of 220 ** the file will actually be a seek to the value given by psf->fileoffset. 221 */ 222 223 print_test_name ("Testing file offset") ; 224 225 /* Test file open in read/write mode. */ 226 psf->file.mode = SFM_RDWR ; 227 psf->fileoffset = sizeof (data_out [0]) * ARRAY_LEN (data_out) ; 228 test_open_or_die (psf, __LINE__) ; 229 230 if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out)) 231 { printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 3 * sizeof (data_out)) ; 232 exit (1) ; 233 } ; 234 235 test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ; 236 make_data (data_out, ARRAY_LEN (data_out), 5) ; 237 test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 2 * sizeof (data_out), __LINE__) ; 238 test_close_or_die (psf, __LINE__) ; 239 240 /* final test with psf->fileoffset == 0. */ 241 242 psf->file.mode = SFM_RDWR ; 243 psf->fileoffset = 0 ; 244 test_open_or_die (psf, __LINE__) ; 245 246 if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out)) 247 { printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %zd)\n\n", __LINE__, retval, 3 * sizeof (data_out)) ; 248 exit (1) ; 249 } ; 250 251 make_data (data_out, ARRAY_LEN (data_out), 1) ; 252 test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; 253 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 254 255 make_data (data_out, ARRAY_LEN (data_out), 2) ; 256 test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ; 257 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 258 259 make_data (data_out, ARRAY_LEN (data_out), 5) ; 260 test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ; 261 test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; 262 263 test_close_or_die (psf, __LINE__) ; 264 265 puts ("ok") ; 266} /* file_read_write_test */ 267 268static void 269file_truncate_test (const char *filename) 270{ SF_PRIVATE sf_data, *psf ; 271 unsigned char buffer [256] ; 272 int k ; 273 274 /* 275 ** Open a new file and write two blocks of data to the file. After each 276 ** write, test that psf_get_filelen() returns the new length. 277 */ 278 279 print_test_name ("Testing file truncate") ; 280 281 memset (&sf_data, 0, sizeof (sf_data)) ; 282 memset (buffer, 0xEE, sizeof (buffer)) ; 283 284 psf = &sf_data ; 285 snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ; 286 287 /* 288 ** Open the file write mode, write 0xEE data and then extend the file 289 ** using truncate (the extended data should be 0x00). 290 */ 291 psf->file.mode = SFM_WRITE ; 292 test_open_or_die (psf, __LINE__) ; 293 test_write_or_die (psf, buffer, sizeof (buffer) / 2, 1, sizeof (buffer) / 2, __LINE__) ; 294 psf_ftruncate (psf, sizeof (buffer)) ; 295 test_close_or_die (psf, __LINE__) ; 296 297 /* Open the file in read mode and check the data. */ 298 psf->file.mode = SFM_READ ; 299 test_open_or_die (psf, __LINE__) ; 300 test_read_or_die (psf, buffer, sizeof (buffer), 1, sizeof (buffer), __LINE__) ; 301 test_close_or_die (psf, __LINE__) ; 302 303 for (k = 0 ; k < SIGNED_SIZEOF (buffer) / 2 ; k++) 304 if (buffer [k] != 0xEE) 305 { printf ("\n\nLine %d : buffer [%d] = %hhu (should be 0xEE)\n\n", __LINE__, k, buffer [k]) ; 306 exit (1) ; 307 } ; 308 309 for (k = SIGNED_SIZEOF (buffer) / 2 ; k < SIGNED_SIZEOF (buffer) ; k++) 310 if (buffer [k] != 0) 311 { printf ("\n\nLine %d : buffer [%d] = %hhu (should be 0)\n\n", __LINE__, k, buffer [k]) ; 312 exit (1) ; 313 } ; 314 315 /* Open the file in read/write and shorten the file using truncate. */ 316 psf->file.mode = SFM_RDWR ; 317 test_open_or_die (psf, __LINE__) ; 318 psf_ftruncate (psf, sizeof (buffer) / 4) ; 319 test_close_or_die (psf, __LINE__) ; 320 321 /* Check the file length. */ 322 psf->file.mode = SFM_READ ; 323 test_open_or_die (psf, __LINE__) ; 324 test_seek_or_die (psf, 0, SEEK_END, SIGNED_SIZEOF (buffer) / 4, __LINE__) ; 325 test_close_or_die (psf, __LINE__) ; 326 327 puts ("ok") ; 328} /* file_truncate_test */ 329 330static void 331file_seek_with_offset_test (const char *filename) 332{ SF_PRIVATE sf_data, *psf ; 333 sf_count_t real_end ; 334 const size_t fileoffset = 64 ; 335 336 print_test_name ("Testing seek with offset") ; 337 338 /* Open the file created by the previous test for reading. */ 339 memset (&sf_data, 0, sizeof (sf_data)) ; 340 psf = &sf_data ; 341 psf->file.mode = SFM_READ ; 342 snprintf (psf->file.path, sizeof (psf->file.path), "%s", filename) ; 343 test_open_or_die (psf, __LINE__) ; 344 345 /* Gather basic info before setting offset. */ 346 real_end = psf_fseek (psf, 0, SEEK_END) ; 347 test_tell_or_die (psf, real_end, __LINE__) ; 348 349 /* Set the fileoffset (usually in a real system this is due to an id3 tag). */ 350 psf->fileoffset = fileoffset ; 351 352 /* Check tell respects offset. */ 353 test_tell_or_die (psf, real_end - fileoffset, __LINE__) ; 354 355 /* Check seeking works as expected. */ 356 test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ; 357 test_seek_or_die (psf, 0, SEEK_CUR, 0, __LINE__) ; 358 test_seek_or_die (psf, 0, SEEK_CUR, 0, __LINE__) ; 359 test_seek_or_die (psf, 0, SEEK_END, real_end - fileoffset, __LINE__) ; 360 361 test_close_or_die (psf, __LINE__) ; 362 363 puts ("ok") ; 364} /* file_seek_with_offset_test */ 365 366/*============================================================================== 367** Testing helper functions. 368*/ 369 370static void 371test_open_or_die (SF_PRIVATE *psf, int linenum) 372{ int error ; 373 374 /* Test that open for read fails if the file doesn't exist. */ 375 error = psf_fopen (psf) ; 376 if (error) 377 { printf ("\n\nLine %d: psf_fopen() failed : %s\n\n", linenum, strerror (errno)) ; 378 exit (1) ; 379 } ; 380 381} /* test_open_or_die */ 382 383static void 384test_close_or_die (SF_PRIVATE *psf, int linenum) 385{ 386 psf_fclose (psf) ; 387 if (psf_file_valid (psf)) 388 { printf ("\n\nLine %d: psf->file.filedes should not be valid.\n\n", linenum) ; 389 exit (1) ; 390 } ; 391 392} /* test_close_or_die */ 393 394static void 395test_write_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) 396{ sf_count_t retval ; 397 398 retval = psf_fwrite (data, bytes, items, psf) ; 399 if (retval != items) 400 { printf ("\n\nLine %d: psf_write() returned %" PRId64 " (should be %" PRId64 ")\n\n", linenum, retval, items) ; 401 exit (1) ; 402 } ; 403 404 if ((retval = psf_ftell (psf)) != new_position) 405 { printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %" PRId64 ")\n\n", linenum, retval, new_position) ; 406 exit (1) ; 407 } ; 408 409 return ; 410} /* test_write_or_die */ 411 412static void 413test_read_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) 414{ sf_count_t retval ; 415 416 retval = psf_fread (data, bytes, items, psf) ; 417 if (retval != items) 418 { printf ("\n\nLine %d: psf_write() returned %" PRId64 " (should be %" PRId64 ")\n\n", linenum, retval, items) ; 419 exit (1) ; 420 } ; 421 422 if ((retval = psf_ftell (psf)) != new_position) 423 { printf ("\n\nLine %d: file length after write is not correct. (%" PRId64 " should be %" PRId64 ")\n\n", linenum, retval, new_position) ; 424 exit (1) ; 425 } ; 426 427 return ; 428} /* test_write_or_die */ 429 430static void 431test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) 432{ sf_count_t retval ; 433 434 retval = psf_fseek (psf, offset, whence) ; 435 436 if (retval != new_position) 437 { printf ("\n\nLine %d: psf_fseek() failed. New position is %" PRId64 " (should be %" PRId64 ").\n\n", 438 linenum, retval, new_position) ; 439 exit (1) ; 440 } ; 441 442} /* test_seek_or_die */ 443 444static void 445test_tell_or_die (SF_PRIVATE *psf, sf_count_t expected_position, int linenum) 446{ 447 sf_count_t retval ; 448 449 retval = psf_ftell (psf) ; 450 451 if (retval != expected_position) 452 { printf ("\n\nLine %d: psf_ftell() failed. Position reported as %" PRId64 " (should be %" PRId64 ").\n\n", 453 linenum, retval, expected_position) ; 454 exit (1) ; 455 } ; 456} 457 458static void 459test_equal_or_die (int *array1, int *array2, int len, int linenum) 460{ int k ; 461 462 for (k = 0 ; k < len ; k++) 463 if (array1 [k] != array2 [k]) 464 printf ("\n\nLine %d: error at index %d (%d != %d).\n\n", 465 linenum, k, array1 [k], array2 [k]) ; 466 467 return ; 468} /* test_equal_or_die */ 469 470static void 471make_data (int *data, int len, int seed) 472{ int k ; 473 474 srand (seed * 3333333 + 14756123) ; 475 476 for (k = 0 ; k < len ; k++) 477 data [k] = rand () ; 478 479} /* make_data */ 480 481void 482test_file_io (void) 483{ const char *filename = "file_io.dat" ; 484 485 file_open_test (filename) ; 486 file_read_write_test (filename) ; 487 file_seek_with_offset_test (filename) ; 488 file_truncate_test (filename) ; 489 490 unlink (filename) ; 491} /* main */ 492 493