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
38 static void make_data (int *data, int len, int seed) ;
39
40 static void file_open_test (const char *filename) ;
41 static void file_read_write_test (const char *filename) ;
42 static void file_truncate_test (const char *filename) ;
43
44 static void test_open_or_die (SF_PRIVATE *psf, int linenum) ;
45 static void test_close_or_die (SF_PRIVATE *psf, int linenum) ;
46
47 static 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) ;
48 static 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) ;
49 static void test_equal_or_die (int *array1, int *array2, int len, int linenum) ;
50 static void test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) ;
51 static 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
59 static void
file_open_test(const char * filename)60 file_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.c, sizeof (psf->file.path.c), "%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.c) ;
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.c) ;
109 puts ("ok") ;
110 } /* file_open_test */
111
112 static void
file_read_write_test(const char * filename)113 file_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.c, sizeof (psf->file.path.c), "%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
268 static void
file_truncate_test(const char * filename)269 file_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.c, sizeof (psf->file.path.c), "%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] = %d (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] = %d (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
330 static void
file_seek_with_offset_test(const char * filename)331 file_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.c, sizeof (psf->file.path.c), "%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
370 static void
test_open_or_die(SF_PRIVATE * psf,int linenum)371 test_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
383 static void
test_close_or_die(SF_PRIVATE * psf,int linenum)384 test_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
394 static 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)395 test_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
412 static 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)413 test_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
430 static void
test_seek_or_die(SF_PRIVATE * psf,sf_count_t offset,int whence,sf_count_t new_position,int linenum)431 test_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
444 static void
test_tell_or_die(SF_PRIVATE * psf,sf_count_t expected_position,int linenum)445 test_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
458 static void
test_equal_or_die(int * array1,int * array2,int len,int linenum)459 test_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
470 static void
make_data(int * data,int len,int seed)471 make_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
481 void
test_file_io(void)482 test_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