xref: /third_party/libsnd/src/test_file_io.c (revision b815c7f3)
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