• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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