1/* test_libFLAC++ - Unit tester for libFLAC++
2 * Copyright (C) 2002,2003,2004,2005,2006,2007  Josh Coalson
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (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 General Public License for more details.
13 *
14 * You should have received a copy of the GNU 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#if HAVE_CONFIG_H
20#  include <config.h>
21#endif
22
23#include <stdio.h>
24#include <stdlib.h> /* for malloc() */
25#include <string.h> /* for memcpy()/memset() */
26#if defined _MSC_VER || defined __MINGW32__
27#include <sys/utime.h> /* for utime() */
28#include <io.h> /* for chmod() */
29#if _MSC_VER <= 1600 /* @@@ [2G limit] */
30#define fseeko fseek
31#define ftello ftell
32#endif
33#else
34#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
35#include <utime.h> /* for utime() */
36#include <unistd.h> /* for chown(), unlink() */
37#endif
38#include <sys/stat.h> /* for stat(), maybe chmod() */
39#include "FLAC/assert.h"
40#include "FLAC++/decoder.h"
41#include "FLAC++/metadata.h"
42#include "share/grabbag.h"
43extern "C" {
44#include "test_libs_common/file_utils_flac.h"
45}
46
47/******************************************************************************
48	The general strategy of these tests (for interface levels 1 and 2) is
49	to create a dummy FLAC file with a known set of initial metadata
50	blocks, then keep a mirror locally of what we expect the metadata to be
51	after each operation.  Then testing becomes a simple matter of running
52	a FLAC::Decoder::File over the dummy file after each operation, comparing
53	the decoded metadata to what's in our local copy.  If there are any
54	differences in the metadata, or the actual audio data is corrupted, we
55	will catch it while decoding.
56******************************************************************************/
57
58class OurFileDecoder: public FLAC::Decoder::File {
59public:
60	inline OurFileDecoder(bool ignore_metadata): ignore_metadata_(ignore_metadata), error_occurred_(false) { }
61
62	bool ignore_metadata_;
63	bool error_occurred_;
64protected:
65	::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
66	void metadata_callback(const ::FLAC__StreamMetadata *metadata);
67	void error_callback(::FLAC__StreamDecoderErrorStatus status);
68};
69
70struct OurMetadata {
71	FLAC::Metadata::Prototype *blocks[64];
72	unsigned num_blocks;
73};
74
75/* our copy of the metadata in flacfilename() */
76static OurMetadata our_metadata_;
77
78/* the current block number that corresponds to the position of the iterator we are testing */
79static unsigned mc_our_block_number_ = 0;
80
81static const char *flacfilename(bool is_ogg)
82{
83	return is_ogg? "metadata.oga" : "metadata.flac";
84}
85
86static bool die_(const char *msg)
87{
88	printf("ERROR: %s\n", msg);
89	return false;
90}
91
92static bool die_c_(const char *msg, FLAC::Metadata::Chain::Status status)
93{
94	printf("ERROR: %s\n", msg);
95	printf("       status=%u (%s)\n", (unsigned)((::FLAC__Metadata_ChainStatus)status), status.as_cstring());
96	return false;
97}
98
99static bool die_ss_(const char *msg, FLAC::Metadata::SimpleIterator &iterator)
100{
101	const FLAC::Metadata::SimpleIterator::Status status = iterator.status();
102	printf("ERROR: %s\n", msg);
103	printf("       status=%u (%s)\n", (unsigned)((::FLAC__Metadata_SimpleIteratorStatus)status), status.as_cstring());
104	return false;
105}
106
107static void *malloc_or_die_(size_t size)
108{
109	void *x = malloc(size);
110	if(0 == x) {
111		fprintf(stderr, "ERROR: out of memory allocating %u bytes\n", (unsigned)size);
112		exit(1);
113	}
114	return x;
115}
116
117static char *strdup_or_die_(const char *s)
118{
119	char *x = strdup(s);
120	if(0 == x) {
121		fprintf(stderr, "ERROR: out of memory copying string \"%s\"\n", s);
122		exit(1);
123	}
124	return x;
125}
126
127/* functions for working with our metadata copy */
128
129static bool replace_in_our_metadata_(FLAC::Metadata::Prototype *block, unsigned position, bool copy)
130{
131	unsigned i;
132	FLAC::Metadata::Prototype *obj = block;
133	FLAC__ASSERT(position < our_metadata_.num_blocks);
134	if(copy) {
135		if(0 == (obj = FLAC::Metadata::clone(block)))
136			return die_("during FLAC::Metadata::clone()");
137	}
138	delete our_metadata_.blocks[position];
139	our_metadata_.blocks[position] = obj;
140
141	/* set the is_last flags */
142	for(i = 0; i < our_metadata_.num_blocks - 1; i++)
143		our_metadata_.blocks[i]->set_is_last(false);
144	our_metadata_.blocks[i]->set_is_last(true);
145
146	return true;
147}
148
149static bool insert_to_our_metadata_(FLAC::Metadata::Prototype *block, unsigned position, bool copy)
150{
151	unsigned i;
152	FLAC::Metadata::Prototype *obj = block;
153	if(copy) {
154		if(0 == (obj = FLAC::Metadata::clone(block)))
155			return die_("during FLAC::Metadata::clone()");
156	}
157	if(position > our_metadata_.num_blocks) {
158		position = our_metadata_.num_blocks;
159	}
160	else {
161		for(i = our_metadata_.num_blocks; i > position; i--)
162			our_metadata_.blocks[i] = our_metadata_.blocks[i-1];
163	}
164	our_metadata_.blocks[position] = obj;
165	our_metadata_.num_blocks++;
166
167	/* set the is_last flags */
168	for(i = 0; i < our_metadata_.num_blocks - 1; i++)
169		our_metadata_.blocks[i]->set_is_last(false);
170	our_metadata_.blocks[i]->set_is_last(true);
171
172	return true;
173}
174
175static void delete_from_our_metadata_(unsigned position)
176{
177	unsigned i;
178	FLAC__ASSERT(position < our_metadata_.num_blocks);
179	delete our_metadata_.blocks[position];
180	for(i = position; i < our_metadata_.num_blocks - 1; i++)
181		our_metadata_.blocks[i] = our_metadata_.blocks[i+1];
182	our_metadata_.num_blocks--;
183
184	/* set the is_last flags */
185	if(our_metadata_.num_blocks > 0) {
186		for(i = 0; i < our_metadata_.num_blocks - 1; i++)
187			our_metadata_.blocks[i]->set_is_last(false);
188		our_metadata_.blocks[i]->set_is_last(true);
189	}
190}
191
192void add_to_padding_length_(unsigned index, int delta)
193{
194	FLAC::Metadata::Padding *padding = dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[index]);
195	FLAC__ASSERT(0 != padding);
196	padding->set_length((unsigned)((int)padding->get_length() + delta));
197}
198
199/*
200 * This wad of functions supports filename- and callback-based chain reading/writing.
201 * Everything up to set_file_stats_() is copied from libFLAC/metadata_iterators.c
202 */
203bool open_tempfile_(const char *filename, FILE **tempfile, char **tempfilename)
204{
205	static const char *tempfile_suffix = ".metadata_edit";
206
207	if(0 == (*tempfilename = (char*)malloc(strlen(filename) + strlen(tempfile_suffix) + 1)))
208		return false;
209	strcpy(*tempfilename, filename);
210	strcat(*tempfilename, tempfile_suffix);
211
212	if(0 == (*tempfile = fopen(*tempfilename, "wb")))
213		return false;
214
215	return true;
216}
217
218void cleanup_tempfile_(FILE **tempfile, char **tempfilename)
219{
220	if(0 != *tempfile) {
221		(void)fclose(*tempfile);
222		*tempfile = 0;
223	}
224
225	if(0 != *tempfilename) {
226		(void)unlink(*tempfilename);
227		free(*tempfilename);
228		*tempfilename = 0;
229	}
230}
231
232bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename)
233{
234	FLAC__ASSERT(0 != filename);
235	FLAC__ASSERT(0 != tempfile);
236	FLAC__ASSERT(0 != tempfilename);
237	FLAC__ASSERT(0 != *tempfilename);
238
239	if(0 != *tempfile) {
240		(void)fclose(*tempfile);
241		*tempfile = 0;
242	}
243
244#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
245	/* on some flavors of windows, rename() will fail if the destination already exists */
246	if(unlink(filename) < 0) {
247		cleanup_tempfile_(tempfile, tempfilename);
248		return false;
249	}
250#endif
251
252	if(0 != rename(*tempfilename, filename)) {
253		cleanup_tempfile_(tempfile, tempfilename);
254		return false;
255	}
256
257	cleanup_tempfile_(tempfile, tempfilename);
258
259	return true;
260}
261
262bool get_file_stats_(const char *filename, struct stat *stats)
263{
264	FLAC__ASSERT(0 != filename);
265	FLAC__ASSERT(0 != stats);
266	return (0 == stat(filename, stats));
267}
268
269void set_file_stats_(const char *filename, struct stat *stats)
270{
271	struct utimbuf srctime;
272
273	FLAC__ASSERT(0 != filename);
274	FLAC__ASSERT(0 != stats);
275
276	srctime.actime = stats->st_atime;
277	srctime.modtime = stats->st_mtime;
278	(void)chmod(filename, stats->st_mode);
279	(void)utime(filename, &srctime);
280#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
281	(void)chown(filename, stats->st_uid, (gid_t)(-1));
282	(void)chown(filename, (uid_t)(-1), stats->st_gid);
283#endif
284}
285
286#ifdef FLAC__VALGRIND_TESTING
287static size_t chain_write_cb_(const void *ptr, size_t size, size_t nmemb, ::FLAC__IOHandle handle)
288{
289	FILE *stream = (FILE*)handle;
290	size_t ret = fwrite(ptr, size, nmemb, stream);
291	if(!ferror(stream))
292		fflush(stream);
293	return ret;
294}
295#endif
296
297static int chain_seek_cb_(::FLAC__IOHandle handle, FLAC__int64 offset, int whence)
298{
299	off_t o = (off_t)offset;
300	FLAC__ASSERT(offset == o);
301	return fseeko((FILE*)handle, o, whence);
302}
303
304static FLAC__int64 chain_tell_cb_(::FLAC__IOHandle handle)
305{
306	return ftello((FILE*)handle);
307}
308
309static int chain_eof_cb_(::FLAC__IOHandle handle)
310{
311	return feof((FILE*)handle);
312}
313
314static bool write_chain_(FLAC::Metadata::Chain &chain, bool use_padding, bool preserve_file_stats, bool filename_based, const char *filename)
315{
316	if(filename_based)
317		return chain.write(use_padding, preserve_file_stats);
318	else {
319		::FLAC__IOCallbacks callbacks;
320
321		memset(&callbacks, 0, sizeof(callbacks));
322		callbacks.read = (::FLAC__IOCallback_Read)fread;
323#ifdef FLAC__VALGRIND_TESTING
324		callbacks.write = chain_write_cb_;
325#else
326		callbacks.write = (::FLAC__IOCallback_Write)fwrite;
327#endif
328		callbacks.seek = chain_seek_cb_;
329		callbacks.eof = chain_eof_cb_;
330
331		if(chain.check_if_tempfile_needed(use_padding)) {
332			struct stat stats;
333			FILE *file, *tempfile;
334			char *tempfilename;
335			if(preserve_file_stats) {
336				if(!get_file_stats_(filename, &stats))
337					return false;
338			}
339			if(0 == (file = fopen(filename, "rb")))
340				return false; /*@@@@ chain status still says OK though */
341			if(!open_tempfile_(filename, &tempfile, &tempfilename)) {
342				fclose(file);
343				cleanup_tempfile_(&tempfile, &tempfilename);
344				return false; /*@@@@ chain status still says OK though */
345			}
346			if(!chain.write(use_padding, (::FLAC__IOHandle)file, callbacks, (::FLAC__IOHandle)tempfile, callbacks)) {
347				fclose(file);
348				fclose(tempfile);
349				return false;
350			}
351			fclose(file);
352			fclose(tempfile);
353			file = tempfile = 0;
354			if(!transport_tempfile_(filename, &tempfile, &tempfilename))
355				return false;
356			if(preserve_file_stats)
357				set_file_stats_(filename, &stats);
358		}
359		else {
360			FILE *file = fopen(filename, "r+b");
361			if(0 == file)
362				return false; /*@@@@ chain status still says OK though */
363			if(!chain.write(use_padding, (::FLAC__IOHandle)file, callbacks))
364				return false;
365			fclose(file);
366		}
367	}
368
369	return true;
370}
371
372static bool read_chain_(FLAC::Metadata::Chain &chain, const char *filename, bool filename_based, bool is_ogg)
373{
374	if(filename_based)
375		return chain.read(filename, is_ogg);
376	else {
377		::FLAC__IOCallbacks callbacks;
378
379		memset(&callbacks, 0, sizeof(callbacks));
380		callbacks.read = (::FLAC__IOCallback_Read)fread;
381		callbacks.seek = chain_seek_cb_;
382		callbacks.tell = chain_tell_cb_;
383
384		{
385			bool ret;
386			FILE *file = fopen(filename, "rb");
387			if(0 == file)
388				return false; /*@@@@ chain status still says OK though */
389			ret = chain.read((::FLAC__IOHandle)file, callbacks, is_ogg);
390			fclose(file);
391			return ret;
392		}
393	}
394}
395
396/* function for comparing our metadata to a FLAC::Metadata::Chain */
397
398static bool compare_chain_(FLAC::Metadata::Chain &chain, unsigned current_position, FLAC::Metadata::Prototype *current_block)
399{
400	unsigned i;
401	FLAC::Metadata::Iterator iterator;
402	bool next_ok = true;
403
404	printf("\tcomparing chain... ");
405	fflush(stdout);
406
407	if(!iterator.is_valid())
408		return die_("allocating memory for iterator");
409
410	iterator.init(chain);
411
412	i = 0;
413	do {
414		FLAC::Metadata::Prototype *block;
415
416		printf("%u... ", i);
417		fflush(stdout);
418
419		if(0 == (block = iterator.get_block()))
420			return die_("getting block from iterator");
421
422		if(*block != *our_metadata_.blocks[i])
423			return die_("metadata block mismatch");
424
425		delete block;
426		i++;
427		next_ok = iterator.next();
428	} while(i < our_metadata_.num_blocks && next_ok);
429
430	if(next_ok)
431		return die_("chain has more blocks than expected");
432
433	if(i < our_metadata_.num_blocks)
434		return die_("short block count in chain");
435
436	if(0 != current_block) {
437		printf("CURRENT_POSITION... ");
438		fflush(stdout);
439
440		if(*current_block != *our_metadata_.blocks[current_position])
441			return die_("metadata block mismatch");
442	}
443
444	printf("PASSED\n");
445
446	return true;
447}
448
449::FLAC__StreamDecoderWriteStatus OurFileDecoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[])
450{
451	(void)buffer;
452
453	if(
454		(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER && frame->header.number.frame_number == 0) ||
455		(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER && frame->header.number.sample_number == 0)
456	) {
457		printf("content... ");
458		fflush(stdout);
459	}
460
461	return ::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
462}
463
464void OurFileDecoder::metadata_callback(const ::FLAC__StreamMetadata *metadata)
465{
466	/* don't bother checking if we've already hit an error */
467	if(error_occurred_)
468		return;
469
470	printf("%d... ", mc_our_block_number_);
471	fflush(stdout);
472
473	if(!ignore_metadata_) {
474		if(mc_our_block_number_ >= our_metadata_.num_blocks) {
475			(void)die_("got more metadata blocks than expected");
476			error_occurred_ = true;
477		}
478		else {
479			if(*our_metadata_.blocks[mc_our_block_number_] != metadata) {
480				(void)die_("metadata block mismatch");
481				error_occurred_ = true;
482			}
483		}
484	}
485
486	mc_our_block_number_++;
487}
488
489void OurFileDecoder::error_callback(::FLAC__StreamDecoderErrorStatus status)
490{
491	error_occurred_ = true;
492	printf("ERROR: got error callback, status = %s (%u)\n", FLAC__StreamDecoderErrorStatusString[status], (unsigned)status);
493}
494
495static bool generate_file_(bool include_extras, bool is_ogg)
496{
497	::FLAC__StreamMetadata streaminfo, vorbiscomment, *cuesheet, picture, padding;
498	::FLAC__StreamMetadata *metadata[4];
499	unsigned i = 0, n = 0;
500
501	printf("generating %sFLAC file for test\n", is_ogg? "Ogg " : "");
502
503	while(our_metadata_.num_blocks > 0)
504		delete_from_our_metadata_(0);
505
506	streaminfo.is_last = false;
507	streaminfo.type = ::FLAC__METADATA_TYPE_STREAMINFO;
508	streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
509	streaminfo.data.stream_info.min_blocksize = 576;
510	streaminfo.data.stream_info.max_blocksize = 576;
511	streaminfo.data.stream_info.min_framesize = 0;
512	streaminfo.data.stream_info.max_framesize = 0;
513	streaminfo.data.stream_info.sample_rate = 44100;
514	streaminfo.data.stream_info.channels = 1;
515	streaminfo.data.stream_info.bits_per_sample = 8;
516	streaminfo.data.stream_info.total_samples = 0;
517	memset(streaminfo.data.stream_info.md5sum, 0, 16);
518
519	{
520		const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING);
521		vorbiscomment.is_last = false;
522		vorbiscomment.type = ::FLAC__METADATA_TYPE_VORBIS_COMMENT;
523		vorbiscomment.length = (4 + vendor_string_length) + 4;
524		vorbiscomment.data.vorbis_comment.vendor_string.length = vendor_string_length;
525		vorbiscomment.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1);
526		memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1);
527		vorbiscomment.data.vorbis_comment.num_comments = 0;
528		vorbiscomment.data.vorbis_comment.comments = 0;
529	}
530
531	{
532		if (0 == (cuesheet = ::FLAC__metadata_object_new(::FLAC__METADATA_TYPE_CUESHEET)))
533			return die_("priming our metadata");
534		cuesheet->is_last = false;
535		strcpy(cuesheet->data.cue_sheet.media_catalog_number, "bogo-MCN");
536		cuesheet->data.cue_sheet.lead_in = 123;
537		cuesheet->data.cue_sheet.is_cd = false;
538		if (!FLAC__metadata_object_cuesheet_insert_blank_track(cuesheet, 0))
539			return die_("priming our metadata");
540		cuesheet->data.cue_sheet.tracks[0].number = 1;
541		if (!FLAC__metadata_object_cuesheet_track_insert_blank_index(cuesheet, 0, 0))
542			return die_("priming our metadata");
543	}
544
545	{
546		picture.is_last = false;
547		picture.type = ::FLAC__METADATA_TYPE_PICTURE;
548		picture.length =
549			(
550				FLAC__STREAM_METADATA_PICTURE_TYPE_LEN +
551				FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* will add the length for the string later */
552				FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* will add the length for the string later */
553				FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN +
554				FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN +
555				FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN +
556				FLAC__STREAM_METADATA_PICTURE_COLORS_LEN +
557				FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN /* will add the length for the data later */
558			) / 8
559		;
560		picture.data.picture.type = ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
561		picture.data.picture.mime_type = strdup_or_die_("image/jpeg");
562		picture.length += strlen(picture.data.picture.mime_type);
563		picture.data.picture.description = (FLAC__byte*)strdup_or_die_("desc");
564		picture.length += strlen((const char *)picture.data.picture.description);
565		picture.data.picture.width = 300;
566		picture.data.picture.height = 300;
567		picture.data.picture.depth = 24;
568		picture.data.picture.colors = 0;
569		picture.data.picture.data = (FLAC__byte*)strdup_or_die_("SOMEJPEGDATA");
570		picture.data.picture.data_length = strlen((const char *)picture.data.picture.data);
571		picture.length += picture.data.picture.data_length;
572	}
573
574	padding.is_last = true;
575	padding.type = ::FLAC__METADATA_TYPE_PADDING;
576	padding.length = 1234;
577
578	metadata[n++] = &vorbiscomment;
579	if(include_extras) {
580		metadata[n++] = cuesheet;
581		metadata[n++] = &picture;
582	}
583	metadata[n++] = &padding;
584
585	FLAC::Metadata::StreamInfo s(&streaminfo);
586	FLAC::Metadata::VorbisComment v(&vorbiscomment);
587	FLAC::Metadata::CueSheet c(cuesheet, /*copy=*/false);
588	FLAC::Metadata::Picture pi(&picture);
589	FLAC::Metadata::Padding p(&padding);
590	if(
591		!insert_to_our_metadata_(&s, i++, /*copy=*/true) ||
592		!insert_to_our_metadata_(&v, i++, /*copy=*/true) ||
593		(include_extras && !insert_to_our_metadata_(&c, i++, /*copy=*/true)) ||
594		(include_extras && !insert_to_our_metadata_(&pi, i++, /*copy=*/true)) ||
595		!insert_to_our_metadata_(&p, i++, /*copy=*/true)
596	)
597		return die_("priming our metadata");
598
599	if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg), 0, 512 * 1024, &streaminfo, metadata, n))
600		return die_("creating the encoded file");
601
602	free(vorbiscomment.data.vorbis_comment.vendor_string.entry);
603	free(picture.data.picture.mime_type);
604	free(picture.data.picture.description);
605	free(picture.data.picture.data);
606
607	return true;
608}
609
610static bool test_file_(bool is_ogg, bool ignore_metadata)
611{
612	const char *filename = flacfilename(is_ogg);
613	OurFileDecoder decoder(ignore_metadata);
614
615	mc_our_block_number_ = 0;
616	decoder.error_occurred_ = false;
617
618	printf("\ttesting '%s'... ", filename);
619	fflush(stdout);
620
621	if(!decoder.is_valid())
622		return die_("couldn't allocate decoder instance");
623
624	decoder.set_md5_checking(true);
625	decoder.set_metadata_respond_all();
626	if((is_ogg? decoder.init_ogg(filename) : decoder.init(filename)) != ::FLAC__STREAM_DECODER_INIT_STATUS_OK) {
627		(void)decoder.finish();
628		return die_("initializing decoder\n");
629	}
630	if(!decoder.process_until_end_of_stream()) {
631		(void)decoder.finish();
632		return die_("decoding file\n");
633	}
634
635	(void)decoder.finish();
636
637	if(decoder.error_occurred_)
638		return false;
639
640	if(mc_our_block_number_ != our_metadata_.num_blocks)
641		return die_("short metadata block count");
642
643	printf("PASSED\n");
644	return true;
645}
646
647static bool change_stats_(const char *filename, bool read_only)
648{
649	if(!grabbag__file_change_stats(filename, read_only))
650		return die_("during grabbag__file_change_stats()");
651
652	return true;
653}
654
655static bool remove_file_(const char *filename)
656{
657	while(our_metadata_.num_blocks > 0)
658		delete_from_our_metadata_(0);
659
660	if(!grabbag__file_remove_file(filename))
661		return die_("removing file");
662
663	return true;
664}
665
666static bool test_level_0_()
667{
668	FLAC::Metadata::StreamInfo streaminfo;
669
670	printf("\n\n++++++ testing level 0 interface\n");
671
672	if(!generate_file_(/*include_extras=*/true, /*is_ogg=*/false))
673		return false;
674
675	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/true))
676		return false;
677
678	printf("testing FLAC::Metadata::get_streaminfo()... ");
679
680	if(!FLAC::Metadata::get_streaminfo(flacfilename(/*is_ogg=*/false), streaminfo))
681		return die_("during FLAC::Metadata::get_streaminfo()");
682
683	/* check to see if some basic data matches (c.f. generate_file_()) */
684	if(streaminfo.get_channels() != 1)
685		return die_("mismatch in streaminfo.get_channels()");
686	if(streaminfo.get_bits_per_sample() != 8)
687		return die_("mismatch in streaminfo.get_bits_per_sample()");
688	if(streaminfo.get_sample_rate() != 44100)
689		return die_("mismatch in streaminfo.get_sample_rate()");
690	if(streaminfo.get_min_blocksize() != 576)
691		return die_("mismatch in streaminfo.get_min_blocksize()");
692	if(streaminfo.get_max_blocksize() != 576)
693		return die_("mismatch in streaminfo.get_max_blocksize()");
694
695	printf("OK\n");
696
697	{
698		printf("testing FLAC::Metadata::get_tags(VorbisComment *&)... ");
699
700		FLAC::Metadata::VorbisComment *tags = 0;
701
702		if(!FLAC::Metadata::get_tags(flacfilename(/*is_ogg=*/false), tags))
703			return die_("during FLAC::Metadata::get_tags()");
704
705		/* check to see if some basic data matches (c.f. generate_file_()) */
706		if(tags->get_num_comments() != 0)
707			return die_("mismatch in tags->get_num_comments()");
708
709		printf("OK\n");
710
711		delete tags;
712	}
713
714	{
715		printf("testing FLAC::Metadata::get_tags(VorbisComment &)... ");
716
717		FLAC::Metadata::VorbisComment tags;
718
719		if(!FLAC::Metadata::get_tags(flacfilename(/*is_ogg=*/false), tags))
720			return die_("during FLAC::Metadata::get_tags()");
721
722		/* check to see if some basic data matches (c.f. generate_file_()) */
723		if(tags.get_num_comments() != 0)
724			return die_("mismatch in tags.get_num_comments()");
725
726		printf("OK\n");
727	}
728
729	{
730		printf("testing FLAC::Metadata::get_cuesheet(CueSheet *&)... ");
731
732		FLAC::Metadata::CueSheet *cuesheet = 0;
733
734		if(!FLAC::Metadata::get_cuesheet(flacfilename(/*is_ogg=*/false), cuesheet))
735			return die_("during FLAC::Metadata::get_cuesheet()");
736
737		/* check to see if some basic data matches (c.f. generate_file_()) */
738		if(cuesheet->get_lead_in() != 123)
739			return die_("mismatch in cuesheet->get_lead_in()");
740
741		printf("OK\n");
742
743		delete cuesheet;
744	}
745
746	{
747		printf("testing FLAC::Metadata::get_cuesheet(CueSheet &)... ");
748
749		FLAC::Metadata::CueSheet cuesheet;
750
751		if(!FLAC::Metadata::get_cuesheet(flacfilename(/*is_ogg=*/false), cuesheet))
752			return die_("during FLAC::Metadata::get_cuesheet()");
753
754		/* check to see if some basic data matches (c.f. generate_file_()) */
755		if(cuesheet.get_lead_in() != 123)
756			return die_("mismatch in cuesheet.get_lead_in()");
757
758		printf("OK\n");
759	}
760
761	{
762		printf("testing FLAC::Metadata::get_picture(Picture *&)... ");
763
764		FLAC::Metadata::Picture *picture = 0;
765
766		if(!FLAC::Metadata::get_picture(flacfilename(/*is_ogg=*/false), picture, /*type=*/(::FLAC__StreamMetadata_Picture_Type)(-1), /*mime_type=*/0, /*description=*/0, /*max_width=*/(unsigned)(-1), /*max_height=*/(unsigned)(-1), /*max_depth=*/(unsigned)(-1), /*max_colors=*/(unsigned)(-1)))
767			return die_("during FLAC::Metadata::get_picture()");
768
769		/* check to see if some basic data matches (c.f. generate_file_()) */
770		if(picture->get_type () != ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
771			return die_("mismatch in picture->get_type ()");
772
773		printf("OK\n");
774
775		delete picture;
776	}
777
778	{
779		printf("testing FLAC::Metadata::get_picture(Picture &)... ");
780
781		FLAC::Metadata::Picture picture;
782
783		if(!FLAC::Metadata::get_picture(flacfilename(/*is_ogg=*/false), picture, /*type=*/(::FLAC__StreamMetadata_Picture_Type)(-1), /*mime_type=*/0, /*description=*/0, /*max_width=*/(unsigned)(-1), /*max_height=*/(unsigned)(-1), /*max_depth=*/(unsigned)(-1), /*max_colors=*/(unsigned)(-1)))
784			return die_("during FLAC::Metadata::get_picture()");
785
786		/* check to see if some basic data matches (c.f. generate_file_()) */
787		if(picture.get_type () != ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
788			return die_("mismatch in picture->get_type ()");
789
790		printf("OK\n");
791	}
792
793	if(!remove_file_(flacfilename(/*is_ogg=*/false)))
794		return false;
795
796	return true;
797}
798
799static bool test_level_1_()
800{
801	FLAC::Metadata::Prototype *block;
802	FLAC::Metadata::StreamInfo *streaminfo;
803	FLAC::Metadata::Padding *padding;
804	FLAC::Metadata::Application *app;
805	FLAC__byte data[1000];
806	unsigned our_current_position = 0;
807
808	// initialize 'data' to avoid Valgrind errors
809	memset(data, 0, sizeof(data));
810
811	printf("\n\n++++++ testing level 1 interface\n");
812
813	/************************************************************/
814	{
815	printf("simple iterator on read-only file\n");
816
817	if(!generate_file_(/*include_extras=*/false, /*is_ogg=*/false))
818		return false;
819
820	if(!change_stats_(flacfilename(/*is_ogg=*/false), /*read_only=*/true))
821		return false;
822
823	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/true))
824		return false;
825
826	FLAC::Metadata::SimpleIterator iterator;
827
828	if(!iterator.is_valid())
829		return die_("iterator.is_valid() returned false");
830
831	if(!iterator.init(flacfilename(/*is_ogg=*/false), /*read_only=*/false, /*preserve_file_stats=*/false))
832		return die_("iterator.init() returned false");
833
834	printf("is writable = %u\n", (unsigned)iterator.is_writable());
835	if(iterator.is_writable())
836		return die_("iterator claims file is writable when tester thinks it should not be; are you running as root?\n");
837
838	printf("iterate forwards\n");
839
840	if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
841		return die_("expected STREAMINFO type from iterator.get_block_type()");
842	if(0 == (block = iterator.get_block()))
843		return die_("getting block 0");
844	if(block->get_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
845		return die_("expected STREAMINFO type");
846	if(block->get_is_last())
847		return die_("expected is_last to be false");
848	if(block->get_length() != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
849		return die_("bad STREAMINFO length");
850	/* check to see if some basic data matches (c.f. generate_file_()) */
851	streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
852	FLAC__ASSERT(0 != streaminfo);
853	if(streaminfo->get_channels() != 1)
854		return die_("mismatch in channels");
855	if(streaminfo->get_bits_per_sample() != 8)
856		return die_("mismatch in bits_per_sample");
857	if(streaminfo->get_sample_rate() != 44100)
858		return die_("mismatch in sample_rate");
859	if(streaminfo->get_min_blocksize() != 576)
860		return die_("mismatch in min_blocksize");
861	if(streaminfo->get_max_blocksize() != 576)
862		return die_("mismatch in max_blocksize");
863	// we will delete streaminfo a little later when we're really done with it...
864
865	if(!iterator.next())
866		return die_("forward iterator ended early");
867	our_current_position++;
868
869	if(!iterator.next())
870		return die_("forward iterator ended early");
871	our_current_position++;
872
873	if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_PADDING)
874		return die_("expected PADDING type from iterator.get_block_type()");
875	if(0 == (block = iterator.get_block()))
876		return die_("getting block 1");
877	if(block->get_type() != ::FLAC__METADATA_TYPE_PADDING)
878		return die_("expected PADDING type");
879	if(!block->get_is_last())
880		return die_("expected is_last to be true");
881	/* check to see if some basic data matches (c.f. generate_file_()) */
882	if(block->get_length() != 1234)
883		return die_("bad PADDING length");
884	delete block;
885
886	if(iterator.next())
887		return die_("forward iterator returned true but should have returned false");
888
889	printf("iterate backwards\n");
890	if(!iterator.prev())
891		return die_("reverse iterator ended early");
892	if(!iterator.prev())
893		return die_("reverse iterator ended early");
894	if(iterator.prev())
895		return die_("reverse iterator returned true but should have returned false");
896
897	printf("testing iterator.set_block() on read-only file...\n");
898
899	if(!iterator.set_block(streaminfo, false))
900		printf("PASSED.  iterator.set_block() returned false like it should\n");
901	else
902		return die_("iterator.set_block() returned true but shouldn't have");
903	delete streaminfo;
904	}
905
906	/************************************************************/
907	{
908	printf("simple iterator on writable file\n");
909
910	if(!change_stats_(flacfilename(/*is_ogg=*/false), /*read-only=*/false))
911		return false;
912
913	printf("creating APPLICATION block\n");
914
915	if(0 == (app = new FLAC::Metadata::Application()))
916		return die_("new FLAC::Metadata::Application()");
917	app->set_id((const unsigned char *)"duh");
918
919	printf("creating PADDING block\n");
920
921	if(0 == (padding = new FLAC::Metadata::Padding()))
922		return die_("new FLAC::Metadata::Padding()");
923	padding->set_length(20);
924
925	FLAC::Metadata::SimpleIterator iterator;
926
927	if(!iterator.is_valid())
928		return die_("iterator.is_valid() returned false");
929
930	if(!iterator.init(flacfilename(/*is_ogg=*/false), /*read_only=*/false, /*preserve_file_stats=*/false))
931		return die_("iterator.init() returned false");
932	our_current_position = 0;
933
934	printf("is writable = %u\n", (unsigned)iterator.is_writable());
935
936	printf("[S]VP\ttry to write over STREAMINFO block...\n");
937	if(!iterator.set_block(app, false))
938		printf("\titerator.set_block() returned false like it should\n");
939	else
940		return die_("iterator.set_block() returned true but shouldn't have");
941
942	printf("[S]VP\tnext\n");
943	if(!iterator.next())
944		return die_("iterator ended early\n");
945	our_current_position++;
946
947	printf("S[V]P\tnext\n");
948	if(!iterator.next())
949		return die_("iterator ended early\n");
950	our_current_position++;
951
952	printf("SV[P]\tinsert PADDING after, don't expand into padding\n");
953	padding->set_length(25);
954	if(!iterator.insert_block_after(padding, false))
955		return die_ss_("iterator.insert_block_after(padding, false)", iterator);
956	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
957		return false;
958
959	printf("SVP[P]\tprev\n");
960	if(!iterator.prev())
961		return die_("iterator ended early\n");
962	our_current_position--;
963
964	printf("SV[P]P\tprev\n");
965	if(!iterator.prev())
966		return die_("iterator ended early\n");
967	our_current_position--;
968
969	printf("S[V]PP\tinsert PADDING after, don't expand into padding\n");
970	padding->set_length(30);
971	if(!iterator.insert_block_after(padding, false))
972		return die_ss_("iterator.insert_block_after(padding, false)", iterator);
973	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
974		return false;
975
976	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
977		return false;
978
979	printf("SV[P]PP\tprev\n");
980	if(!iterator.prev())
981		return die_("iterator ended early\n");
982	our_current_position--;
983
984	printf("S[V]PPP\tprev\n");
985	if(!iterator.prev())
986		return die_("iterator ended early\n");
987	our_current_position--;
988
989	printf("[S]VPPP\tdelete (STREAMINFO block), must fail\n");
990	if(iterator.delete_block(false))
991		return die_ss_("iterator.delete_block(false) should have returned false", iterator);
992
993	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
994		return false;
995
996	printf("[S]VPPP\tnext\n");
997	if(!iterator.next())
998		return die_("iterator ended early\n");
999	our_current_position++;
1000
1001	printf("S[V]PPP\tnext\n");
1002	if(!iterator.next())
1003		return die_("iterator ended early\n");
1004	our_current_position++;
1005
1006	printf("SV[P]PP\tdelete (middle block), replace with padding\n");
1007	if(!iterator.delete_block(true))
1008		return die_ss_("iterator.delete_block(true)", iterator);
1009	our_current_position--;
1010
1011	printf("S[V]PPP\tnext\n");
1012	if(!iterator.next())
1013		return die_("iterator ended early\n");
1014	our_current_position++;
1015
1016	printf("SV[P]PP\tdelete (middle block), don't replace with padding\n");
1017	if(!iterator.delete_block(false))
1018		return die_ss_("iterator.delete_block(false)", iterator);
1019	delete_from_our_metadata_(our_current_position--);
1020
1021	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1022		return false;
1023
1024	printf("S[V]PP\tnext\n");
1025	if(!iterator.next())
1026		return die_("iterator ended early\n");
1027	our_current_position++;
1028
1029	printf("SV[P]P\tnext\n");
1030	if(!iterator.next())
1031		return die_("iterator ended early\n");
1032	our_current_position++;
1033
1034	printf("SVP[P]\tdelete (last block), replace with padding\n");
1035	if(!iterator.delete_block(true))
1036		return die_ss_("iterator.delete_block(false)", iterator);
1037	our_current_position--;
1038
1039	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1040		return false;
1041
1042	printf("SV[P]P\tnext\n");
1043	if(!iterator.next())
1044		return die_("iterator ended early\n");
1045	our_current_position++;
1046
1047	printf("SVP[P]\tdelete (last block), don't replace with padding\n");
1048	if(!iterator.delete_block(false))
1049		return die_ss_("iterator.delete_block(false)", iterator);
1050	delete_from_our_metadata_(our_current_position--);
1051
1052	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1053		return false;
1054
1055	printf("SV[P]\tprev\n");
1056	if(!iterator.prev())
1057		return die_("iterator ended early\n");
1058	our_current_position--;
1059
1060	printf("S[V]P\tprev\n");
1061	if(!iterator.prev())
1062		return die_("iterator ended early\n");
1063	our_current_position--;
1064
1065	printf("[S]VP\tset STREAMINFO (change sample rate)\n");
1066	FLAC__ASSERT(our_current_position == 0);
1067	block = iterator.get_block();
1068	streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
1069	FLAC__ASSERT(0 != streaminfo);
1070	streaminfo->set_sample_rate(32000);
1071	if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
1072		return die_("copying object");
1073	if(!iterator.set_block(block, false))
1074		return die_ss_("iterator.set_block(block, false)", iterator);
1075	delete block;
1076
1077	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1078		return false;
1079
1080	printf("[S]VP\tnext\n");
1081	if(!iterator.next())
1082		return die_("iterator ended early\n");
1083	our_current_position++;
1084
1085	printf("S[V]P\tinsert APPLICATION after, expand into padding of exceeding size\n");
1086	app->set_id((const unsigned char *)"euh"); /* twiddle the id so that our comparison doesn't miss transposition */
1087	if(!iterator.insert_block_after(app, true))
1088		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1089	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1090		return false;
1091	add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
1092
1093	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1094		return false;
1095
1096	printf("SV[A]P\tnext\n");
1097	if(!iterator.next())
1098		return die_("iterator ended early\n");
1099	our_current_position++;
1100
1101	printf("SVA[P]\tset APPLICATION, expand into padding of exceeding size\n");
1102	app->set_id((const unsigned char *)"fuh"); /* twiddle the id */
1103	if(!iterator.set_block(app, true))
1104		return die_ss_("iterator.set_block(app, true)", iterator);
1105	if(!insert_to_our_metadata_(app, our_current_position, /*copy=*/true))
1106		return false;
1107	add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
1108
1109	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1110		return false;
1111
1112	printf("SVA[A]P\tset APPLICATION (grow), don't expand into padding\n");
1113	app->set_id((const unsigned char *)"guh"); /* twiddle the id */
1114	if(!app->set_data(data, sizeof(data), true))
1115		return die_("setting APPLICATION data");
1116	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1117		return die_("copying object");
1118	if(!iterator.set_block(app, false))
1119		return die_ss_("iterator.set_block(app, false)", iterator);
1120
1121	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1122		return false;
1123
1124	printf("SVA[A]P\tset APPLICATION (shrink), don't fill in with padding\n");
1125	app->set_id((const unsigned char *)"huh"); /* twiddle the id */
1126	if(!app->set_data(data, 12, true))
1127		return die_("setting APPLICATION data");
1128	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1129		return die_("copying object");
1130	if(!iterator.set_block(app, false))
1131		return die_ss_("iterator.set_block(app, false)", iterator);
1132
1133	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1134		return false;
1135
1136	printf("SVA[A]P\tset APPLICATION (grow), expand into padding of exceeding size\n");
1137	app->set_id((const unsigned char *)"iuh"); /* twiddle the id */
1138	if(!app->set_data(data, sizeof(data), true))
1139		return die_("setting APPLICATION data");
1140	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1141		return die_("copying object");
1142	add_to_padding_length_(our_current_position+1, -((int)sizeof(data) - 12));
1143	if(!iterator.set_block(app, true))
1144		return die_ss_("iterator.set_block(app, true)", iterator);
1145
1146	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1147		return false;
1148
1149	printf("SVA[A]P\tset APPLICATION (shrink), fill in with padding\n");
1150	app->set_id((const unsigned char *)"juh"); /* twiddle the id */
1151	if(!app->set_data(data, 23, true))
1152		return die_("setting APPLICATION data");
1153	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1154		return die_("copying object");
1155	if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/true))
1156		return die_("copying object");
1157	dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(sizeof(data) - 23 - FLAC__STREAM_METADATA_HEADER_LENGTH);
1158	if(!iterator.set_block(app, true))
1159		return die_ss_("iterator.set_block(app, true)", iterator);
1160
1161	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1162		return false;
1163
1164	printf("SVA[A]PP\tnext\n");
1165	if(!iterator.next())
1166		return die_("iterator ended early\n");
1167	our_current_position++;
1168
1169	printf("SVAA[P]P\tnext\n");
1170	if(!iterator.next())
1171		return die_("iterator ended early\n");
1172	our_current_position++;
1173
1174	printf("SVAAP[P]\tset PADDING (shrink), don't fill in with padding\n");
1175	padding->set_length(5);
1176	if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1177		return die_("copying object");
1178	if(!iterator.set_block(padding, false))
1179		return die_ss_("iterator.set_block(padding, false)", iterator);
1180
1181	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1182		return false;
1183
1184	printf("SVAAP[P]\tset APPLICATION (grow)\n");
1185	app->set_id((const unsigned char *)"kuh"); /* twiddle the id */
1186	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1187		return die_("copying object");
1188	if(!iterator.set_block(app, false))
1189		return die_ss_("iterator.set_block(app, false)", iterator);
1190
1191	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1192		return false;
1193
1194	printf("SVAAP[A]\tset PADDING (equal)\n");
1195	padding->set_length(27);
1196	if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1197		return die_("copying object");
1198	if(!iterator.set_block(padding, false))
1199		return die_ss_("iterator.set_block(padding, false)", iterator);
1200
1201	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1202		return false;
1203
1204	printf("SVAAP[P]\tprev\n");
1205	if(!iterator.prev())
1206		return die_("iterator ended early\n");
1207	our_current_position--;
1208
1209	printf("SVAA[P]P\tdelete (middle block), don't replace with padding\n");
1210	if(!iterator.delete_block(false))
1211		return die_ss_("iterator.delete_block(false)", iterator);
1212	delete_from_our_metadata_(our_current_position--);
1213
1214	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1215		return false;
1216
1217	printf("SVA[A]P\tdelete (middle block), don't replace with padding\n");
1218	if(!iterator.delete_block(false))
1219		return die_ss_("iterator.delete_block(false)", iterator);
1220	delete_from_our_metadata_(our_current_position--);
1221
1222	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1223		return false;
1224
1225	printf("SV[A]P\tnext\n");
1226	if(!iterator.next())
1227		return die_("iterator ended early\n");
1228	our_current_position++;
1229
1230	printf("SVA[P]\tinsert PADDING after\n");
1231	padding->set_length(5);
1232	if(!iterator.insert_block_after(padding, false))
1233		return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1234	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1235		return false;
1236
1237	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1238		return false;
1239
1240	printf("SVAP[P]\tprev\n");
1241	if(!iterator.prev())
1242		return die_("iterator ended early\n");
1243	our_current_position--;
1244
1245	printf("SVA[P]P\tprev\n");
1246	if(!iterator.prev())
1247		return die_("iterator ended early\n");
1248	our_current_position--;
1249
1250	printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is too small\n");
1251	if(!app->set_data(data, 32, true))
1252		return die_("setting APPLICATION data");
1253	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1254		return die_("copying object");
1255	if(!iterator.set_block(app, true))
1256		return die_ss_("iterator.set_block(app, true)", iterator);
1257
1258	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1259		return false;
1260
1261	printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is 'close' but still too small\n");
1262	if(!app->set_data(data, 60, true))
1263		return die_("setting APPLICATION data");
1264	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1265		return die_("copying object");
1266	if(!iterator.set_block(app, true))
1267		return die_ss_("iterator.set_block(app, true)", iterator);
1268
1269	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1270		return false;
1271
1272	printf("SV[A]PP\tset APPLICATION (grow), expand into padding which will leave 0-length pad\n");
1273	if(!app->set_data(data, 87, true))
1274		return die_("setting APPLICATION data");
1275	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1276		return die_("copying object");
1277	dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
1278	if(!iterator.set_block(app, true))
1279		return die_ss_("iterator.set_block(app, true)", iterator);
1280
1281	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1282		return false;
1283
1284	printf("SV[A]PP\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
1285	if(!app->set_data(data, 91, true))
1286		return die_("setting APPLICATION data");
1287	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1288		return die_("copying object");
1289	delete_from_our_metadata_(our_current_position+1);
1290	if(!iterator.set_block(app, true))
1291		return die_ss_("iterator.set_block(app, true)", iterator);
1292
1293	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1294		return false;
1295
1296	printf("SV[A]P\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
1297	if(!app->set_data(data, 100, true))
1298		return die_("setting APPLICATION data");
1299	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1300		return die_("copying object");
1301	delete_from_our_metadata_(our_current_position+1);
1302	our_metadata_.blocks[our_current_position]->set_is_last(true);
1303	if(!iterator.set_block(app, true))
1304		return die_ss_("iterator.set_block(app, true)", iterator);
1305
1306	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1307		return false;
1308
1309	printf("SV[A]\tset PADDING (equal size)\n");
1310	padding->set_length(app->get_length());
1311	if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1312		return die_("copying object");
1313	if(!iterator.set_block(padding, true))
1314		return die_ss_("iterator.set_block(padding, true)", iterator);
1315
1316	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1317		return false;
1318
1319	printf("SV[P]\tinsert PADDING after\n");
1320	if(!iterator.insert_block_after(padding, false))
1321		return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1322	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1323		return false;
1324
1325	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1326		return false;
1327
1328	printf("SVP[P]\tinsert PADDING after\n");
1329	padding->set_length(5);
1330	if(!iterator.insert_block_after(padding, false))
1331		return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1332	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1333		return false;
1334
1335	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1336		return false;
1337
1338	printf("SVPP[P]\tprev\n");
1339	if(!iterator.prev())
1340		return die_("iterator ended early\n");
1341	our_current_position--;
1342
1343	printf("SVP[P]P\tprev\n");
1344	if(!iterator.prev())
1345		return die_("iterator ended early\n");
1346	our_current_position--;
1347
1348	printf("SV[P]PP\tprev\n");
1349	if(!iterator.prev())
1350		return die_("iterator ended early\n");
1351	our_current_position--;
1352
1353	printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is too small\n");
1354	if(!app->set_data(data, 101, true))
1355		return die_("setting APPLICATION data");
1356	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1357		return die_("copying object");
1358	if(!iterator.insert_block_after(app, true))
1359		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1360
1361	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1362		return false;
1363
1364	printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
1365	if(!iterator.delete_block(false))
1366		return die_ss_("iterator.delete_block(false)", iterator);
1367	delete_from_our_metadata_(our_current_position--);
1368
1369	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1370		return false;
1371
1372	printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is 'close' but still too small\n");
1373	if(!app->set_data(data, 97, true))
1374		return die_("setting APPLICATION data");
1375	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1376		return die_("copying object");
1377	if(!iterator.insert_block_after(app, true))
1378		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1379
1380	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1381		return false;
1382
1383	printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
1384	if(!iterator.delete_block(false))
1385		return die_ss_("iterator.delete_block(false)", iterator);
1386	delete_from_our_metadata_(our_current_position--);
1387
1388	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1389		return false;
1390
1391	printf("S[V]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1392	if(!app->set_data(data, 100, true))
1393		return die_("setting APPLICATION data");
1394	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1395		return die_("copying object");
1396	delete_from_our_metadata_(our_current_position+1);
1397	if(!iterator.insert_block_after(app, true))
1398		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1399
1400	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1401		return false;
1402
1403	printf("SV[A]PP\tdelete (middle block), don't replace with padding\n");
1404	if(!iterator.delete_block(false))
1405		return die_ss_("iterator.delete_block(false)", iterator);
1406	delete_from_our_metadata_(our_current_position--);
1407
1408	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1409		return false;
1410
1411	printf("S[V]PP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n");
1412	if(!app->set_data(data, 96, true))
1413		return die_("setting APPLICATION data");
1414	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1415		return die_("copying object");
1416	dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
1417	if(!iterator.insert_block_after(app, true))
1418		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1419
1420	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1421		return false;
1422
1423	printf("SV[A]PP\tdelete (middle block), don't replace with padding\n");
1424	if(!iterator.delete_block(false))
1425		return die_ss_("iterator.delete_block(false)", iterator);
1426	delete_from_our_metadata_(our_current_position--);
1427
1428	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1429		return false;
1430
1431	printf("S[V]PP\tnext\n");
1432	if(!iterator.next())
1433		return die_("iterator ended early\n");
1434	our_current_position++;
1435
1436	printf("SV[P]P\tdelete (middle block), don't replace with padding\n");
1437	if(!iterator.delete_block(false))
1438		return die_ss_("iterator.delete_block(false)", iterator);
1439	delete_from_our_metadata_(our_current_position--);
1440
1441	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1442		return false;
1443
1444	printf("S[V]P\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1445	if(!app->set_data(data, 1, true))
1446		return die_("setting APPLICATION data");
1447	if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1448		return die_("copying object");
1449	delete_from_our_metadata_(our_current_position+1);
1450	if(!iterator.insert_block_after(app, true))
1451		return die_ss_("iterator.insert_block_after(app, true)", iterator);
1452
1453	if(!test_file_(/*is_ogg=*/false, /*ignore_metadata=*/false))
1454		return false;
1455	}
1456
1457	delete app;
1458	delete padding;
1459
1460	if(!remove_file_(flacfilename(/*is_ogg=*/false)))
1461		return false;
1462
1463	return true;
1464}
1465
1466static bool test_level_2_(bool filename_based, bool is_ogg)
1467{
1468	FLAC::Metadata::Prototype *block;
1469	FLAC::Metadata::StreamInfo *streaminfo;
1470	FLAC::Metadata::Application *app;
1471	FLAC::Metadata::Padding *padding;
1472	FLAC__byte data[2000];
1473	unsigned our_current_position;
1474
1475	// initialize 'data' to avoid Valgrind errors
1476	memset(data, 0, sizeof(data));
1477
1478	printf("\n\n++++++ testing level 2 interface (%s-based, %s FLAC)\n", filename_based? "filename":"callback", is_ogg? "Ogg":"native");
1479
1480	printf("generate read-only file\n");
1481
1482	if(!generate_file_(/*include_extras=*/false, is_ogg))
1483		return false;
1484
1485	if(!change_stats_(flacfilename(is_ogg), /*read_only=*/true))
1486		return false;
1487
1488	printf("create chain\n");
1489	FLAC::Metadata::Chain chain;
1490	if(!chain.is_valid())
1491		return die_("allocating memory for chain");
1492
1493	printf("read chain\n");
1494
1495	if(!read_chain_(chain, flacfilename(is_ogg), filename_based, is_ogg))
1496		return die_c_("reading chain", chain.status());
1497
1498	printf("[S]VP\ttest initial metadata\n");
1499
1500	if(!compare_chain_(chain, 0, 0))
1501		return false;
1502	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1503		return false;
1504
1505	if(is_ogg)
1506		goto end;
1507
1508	printf("switch file to read-write\n");
1509
1510	if(!change_stats_(flacfilename(is_ogg), /*read-only=*/false))
1511		return false;
1512
1513	printf("create iterator\n");
1514	{
1515	FLAC::Metadata::Iterator iterator;
1516	if(!iterator.is_valid())
1517		return die_("allocating memory for iterator");
1518
1519	our_current_position = 0;
1520
1521	iterator.init(chain);
1522
1523	if(0 == (block = iterator.get_block()))
1524		return die_("getting block from iterator");
1525
1526	FLAC__ASSERT(block->get_type() == FLAC__METADATA_TYPE_STREAMINFO);
1527
1528	printf("[S]VP\tmodify STREAMINFO, write\n");
1529
1530	streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
1531	FLAC__ASSERT(0 != streaminfo);
1532	streaminfo->set_sample_rate(32000);
1533	if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
1534		return die_("copying object");
1535	delete block;
1536
1537	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/true, filename_based, flacfilename(is_ogg)))
1538		return die_c_("during chain.write(false, true)", chain.status());
1539	block = iterator.get_block();
1540	if(!compare_chain_(chain, our_current_position, block))
1541		return false;
1542	delete block;
1543	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1544		return false;
1545
1546	printf("[S]VP\tnext\n");
1547	if(!iterator.next())
1548		return die_("iterator ended early\n");
1549	our_current_position++;
1550
1551	printf("S[V]P\tnext\n");
1552	if(!iterator.next())
1553		return die_("iterator ended early\n");
1554	our_current_position++;
1555
1556	printf("SV[P]\treplace PADDING with identical-size APPLICATION\n");
1557	if(0 == (block = iterator.get_block()))
1558		return die_("getting block from iterator");
1559	if(0 == (app = new FLAC::Metadata::Application()))
1560		return die_("new FLAC::Metadata::Application()");
1561	app->set_id((const unsigned char *)"duh");
1562	if(!app->set_data(data, block->get_length()-(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), true))
1563		return die_("setting APPLICATION data");
1564	delete block;
1565	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1566		return die_("copying object");
1567	if(!iterator.set_block(app))
1568		return die_c_("iterator.set_block(app)", chain.status());
1569
1570	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1571		return die_c_("during chain.write(false, false)", chain.status());
1572	block = iterator.get_block();
1573	if(!compare_chain_(chain, our_current_position, block))
1574		return false;
1575	delete block;
1576	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1577		return false;
1578
1579	printf("SV[A]\tshrink APPLICATION, don't use padding\n");
1580	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1581		return die_("copying object");
1582	if(!app->set_data(data, 26, true))
1583		return die_("setting APPLICATION data");
1584	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1585		return die_("copying object");
1586	if(!iterator.set_block(app))
1587		return die_c_("iterator.set_block(app)", chain.status());
1588
1589	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1590		return die_c_("during chain.write(false, false)", chain.status());
1591	block = iterator.get_block();
1592	if(!compare_chain_(chain, our_current_position, block))
1593		return false;
1594	delete block;
1595	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1596		return false;
1597
1598	printf("SV[A]\tgrow APPLICATION, don't use padding\n");
1599	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1600		return die_("copying object");
1601	if(!app->set_data(data, 28, true))
1602		return die_("setting APPLICATION data");
1603	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1604		return die_("copying object");
1605	if(!iterator.set_block(app))
1606		return die_c_("iterator.set_block(app)", chain.status());
1607
1608	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1609		return die_c_("during chain.write(false, false)", chain.status());
1610	block = iterator.get_block();
1611	if(!compare_chain_(chain, our_current_position, block))
1612		return false;
1613	delete block;
1614	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1615		return false;
1616
1617	printf("SV[A]\tgrow APPLICATION, use padding, but last block is not padding\n");
1618	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1619		return die_("copying object");
1620	if(!app->set_data(data, 36, true))
1621		return die_("setting APPLICATION data");
1622	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1623		return die_("copying object");
1624	if(!iterator.set_block(app))
1625		return die_c_("iterator.set_block(app)", chain.status());
1626
1627	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1628		return die_c_("during chain.write(false, false)", chain.status());
1629	block = iterator.get_block();
1630	if(!compare_chain_(chain, our_current_position, block))
1631		return false;
1632	delete block;
1633	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1634		return false;
1635
1636	printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, but delta is too small for new PADDING block\n");
1637	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1638		return die_("copying object");
1639	if(!app->set_data(data, 33, true))
1640		return die_("setting APPLICATION data");
1641	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1642		return die_("copying object");
1643	if(!iterator.set_block(app))
1644		return die_c_("iterator.set_block(app)", chain.status());
1645
1646	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1647		return die_c_("during chain.write(true, false)", chain.status());
1648	block = iterator.get_block();
1649	if(!compare_chain_(chain, our_current_position, block))
1650		return false;
1651	delete block;
1652	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1653		return false;
1654
1655	printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, delta is enough for new PADDING block\n");
1656	if(0 == (padding = new FLAC::Metadata::Padding()))
1657		return die_("creating PADDING block");
1658	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1659		return die_("copying object");
1660	if(!app->set_data(data, 29, true))
1661		return die_("setting APPLICATION data");
1662	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1663		return die_("copying object");
1664	padding->set_length(0);
1665	if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/false))
1666		return die_("internal error");
1667	if(!iterator.set_block(app))
1668		return die_c_("iterator.set_block(app)", chain.status());
1669
1670	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1671		return die_c_("during chain.write(true, false)", chain.status());
1672	block = iterator.get_block();
1673	if(!compare_chain_(chain, our_current_position, block))
1674		return false;
1675	delete block;
1676	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1677		return false;
1678
1679	printf("SV[A]P\tshrink APPLICATION, use padding, last block is padding\n");
1680	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1681		return die_("copying object");
1682	if(!app->set_data(data, 16, true))
1683		return die_("setting APPLICATION data");
1684	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1685		return die_("copying object");
1686	dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(13);
1687	if(!iterator.set_block(app))
1688		return die_c_("iterator.set_block(app)", chain.status());
1689
1690	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1691		return die_c_("during chain.write(true, false)", chain.status());
1692	block = iterator.get_block();
1693	if(!compare_chain_(chain, our_current_position, block))
1694		return false;
1695	delete block;
1696	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1697		return false;
1698
1699	printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding, but delta is too small\n");
1700	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1701		return die_("copying object");
1702	if(!app->set_data(data, 50, true))
1703		return die_("setting APPLICATION data");
1704	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1705		return die_("copying object");
1706	if(!iterator.set_block(app))
1707		return die_c_("iterator.set_block(app)", chain.status());
1708
1709	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1710		return die_c_("during chain.write(true, false)", chain.status());
1711	block = iterator.get_block();
1712	if(!compare_chain_(chain, our_current_position, block))
1713		return false;
1714	delete block;
1715	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1716		return false;
1717
1718	printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exceeding size\n");
1719	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1720		return die_("copying object");
1721	if(!app->set_data(data, 56, true))
1722		return die_("setting APPLICATION data");
1723	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1724		return die_("copying object");
1725	add_to_padding_length_(our_current_position+1, -(56 - 50));
1726	if(!iterator.set_block(app))
1727		return die_c_("iterator.set_block(app)", chain.status());
1728
1729	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1730		return die_c_("during chain.write(true, false)", chain.status());
1731	block = iterator.get_block();
1732	if(!compare_chain_(chain, our_current_position, block))
1733		return false;
1734	delete block;
1735	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1736		return false;
1737
1738	printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exact size\n");
1739	if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1740		return die_("copying object");
1741	if(!app->set_data(data, 67, true))
1742		return die_("setting APPLICATION data");
1743	if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1744		return die_("copying object");
1745	delete_from_our_metadata_(our_current_position+1);
1746	if(!iterator.set_block(app))
1747		return die_c_("iterator.set_block(app)", chain.status());
1748
1749	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1750		return die_c_("during chain.write(true, false)", chain.status());
1751	block = iterator.get_block();
1752	if(!compare_chain_(chain, our_current_position, block))
1753		return false;
1754	delete block;
1755	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1756		return false;
1757
1758	printf("SV[A]\tprev\n");
1759	if(!iterator.prev())
1760		return die_("iterator ended early\n");
1761	our_current_position--;
1762
1763	printf("S[V]A\tprev\n");
1764	if(!iterator.prev())
1765		return die_("iterator ended early\n");
1766	our_current_position--;
1767
1768	printf("[S]VA\tinsert PADDING before STREAMINFO (should fail)\n");
1769	if(0 == (padding = new FLAC::Metadata::Padding()))
1770		return die_("creating PADDING block");
1771	padding->set_length(30);
1772	if(!iterator.insert_block_before(padding))
1773		printf("\titerator.insert_block_before() returned false like it should\n");
1774	else
1775		return die_("iterator.insert_block_before() should have returned false");
1776
1777	printf("[S]VA\tnext\n");
1778	if(!iterator.next())
1779		return die_("iterator ended early\n");
1780	our_current_position++;
1781
1782	printf("S[V]A\tinsert PADDING after\n");
1783	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1784		return die_("copying metadata");
1785	if(!iterator.insert_block_after(padding))
1786		return die_("iterator.insert_block_after(padding)");
1787
1788	block = iterator.get_block();
1789	if(!compare_chain_(chain, our_current_position, block))
1790		return false;
1791	delete block;
1792
1793	printf("SV[P]A\tinsert PADDING before\n");
1794	if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1795		return die_("creating PADDING block");
1796	padding->set_length(17);
1797	if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1798		return die_("copying metadata");
1799	if(!iterator.insert_block_before(padding))
1800		return die_("iterator.insert_block_before(padding)");
1801
1802	block = iterator.get_block();
1803	if(!compare_chain_(chain, our_current_position, block))
1804		return false;
1805	delete block;
1806
1807	printf("SV[P]PA\tinsert PADDING before\n");
1808	if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1809		return die_("creating PADDING block");
1810	padding->set_length(0);
1811	if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1812		return die_("copying metadata");
1813	if(!iterator.insert_block_before(padding))
1814		return die_("iterator.insert_block_before(padding)");
1815
1816	block = iterator.get_block();
1817	if(!compare_chain_(chain, our_current_position, block))
1818		return false;
1819	delete block;
1820
1821	printf("SV[P]PPA\tnext\n");
1822	if(!iterator.next())
1823		return die_("iterator ended early\n");
1824	our_current_position++;
1825
1826	printf("SVP[P]PA\tnext\n");
1827	if(!iterator.next())
1828		return die_("iterator ended early\n");
1829	our_current_position++;
1830
1831	printf("SVPP[P]A\tnext\n");
1832	if(!iterator.next())
1833		return die_("iterator ended early\n");
1834	our_current_position++;
1835
1836	printf("SVPPP[A]\tinsert PADDING after\n");
1837	if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1838		return die_("creating PADDING block");
1839	padding->set_length(57);
1840	if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1841		return die_("copying metadata");
1842	if(!iterator.insert_block_after(padding))
1843		return die_("iterator.insert_block_after(padding)");
1844
1845	block = iterator.get_block();
1846	if(!compare_chain_(chain, our_current_position, block))
1847		return false;
1848	delete block;
1849
1850	printf("SVPPPA[P]\tinsert PADDING before\n");
1851	if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1852		return die_("creating PADDING block");
1853	padding->set_length(99);
1854	if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1855		return die_("copying metadata");
1856	if(!iterator.insert_block_before(padding))
1857		return die_("iterator.insert_block_before(padding)");
1858
1859	block = iterator.get_block();
1860	if(!compare_chain_(chain, our_current_position, block))
1861		return false;
1862	delete block;
1863
1864	}
1865	our_current_position = 0;
1866
1867	printf("SVPPPAPP\tmerge padding\n");
1868	chain.merge_padding();
1869	add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[3]->get_length());
1870	add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[4]->get_length());
1871	add_to_padding_length_(6, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[7]->get_length());
1872	delete_from_our_metadata_(7);
1873	delete_from_our_metadata_(4);
1874	delete_from_our_metadata_(3);
1875
1876	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1877		return die_c_("during chain.write(true, false)", chain.status());
1878	if(!compare_chain_(chain, 0, 0))
1879		return false;
1880	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1881		return false;
1882
1883	printf("SVPAP\tsort padding\n");
1884	chain.sort_padding();
1885	add_to_padding_length_(4, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[2]->get_length());
1886	delete_from_our_metadata_(2);
1887
1888	if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1889		return die_c_("during chain.write(true, false)", chain.status());
1890	if(!compare_chain_(chain, 0, 0))
1891		return false;
1892	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
1893		return false;
1894
1895	printf("create iterator\n");
1896	{
1897	FLAC::Metadata::Iterator iterator;
1898	if(!iterator.is_valid())
1899		return die_("allocating memory for iterator");
1900
1901	our_current_position = 0;
1902
1903	iterator.init(chain);
1904
1905	printf("[S]VAP\tnext\n");
1906	if(!iterator.next())
1907		return die_("iterator ended early\n");
1908	our_current_position++;
1909
1910	printf("S[V]AP\tnext\n");
1911	if(!iterator.next())
1912		return die_("iterator ended early\n");
1913	our_current_position++;
1914
1915	printf("SV[A]P\tdelete middle block, replace with padding\n");
1916	if(0 == (padding = new FLAC::Metadata::Padding()))
1917		return die_("creating PADDING block");
1918	padding->set_length(71);
1919	if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1920		return die_("copying object");
1921	if(!iterator.delete_block(/*replace_with_padding=*/true))
1922		return die_c_("iterator.delete_block(true)", chain.status());
1923
1924	block = iterator.get_block();
1925	if(!compare_chain_(chain, our_current_position, block))
1926		return false;
1927	delete block;
1928
1929	printf("S[V]PP\tnext\n");
1930	if(!iterator.next())
1931		return die_("iterator ended early\n");
1932	our_current_position++;
1933
1934	printf("SV[P]P\tdelete middle block, don't replace with padding\n");
1935	delete_from_our_metadata_(our_current_position--);
1936	if(!iterator.delete_block(/*replace_with_padding=*/false))
1937		return die_c_("iterator.delete_block(false)", chain.status());
1938
1939	block = iterator.get_block();
1940	if(!compare_chain_(chain, our_current_position, block))
1941		return false;
1942	delete block;
1943
1944	printf("S[V]P\tnext\n");
1945	if(!iterator.next())
1946		return die_("iterator ended early\n");
1947	our_current_position++;
1948
1949	printf("SV[P]\tdelete last block, replace with padding\n");
1950	if(0 == (padding = new FLAC::Metadata::Padding()))
1951		return die_("creating PADDING block");
1952	padding->set_length(219);
1953	if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1954		return die_("copying object");
1955	if(!iterator.delete_block(/*replace_with_padding=*/true))
1956		return die_c_("iterator.delete_block(true)", chain.status());
1957
1958	block = iterator.get_block();
1959	if(!compare_chain_(chain, our_current_position, block))
1960		return false;
1961	delete block;
1962
1963	printf("S[V]P\tnext\n");
1964	if(!iterator.next())
1965		return die_("iterator ended early\n");
1966	our_current_position++;
1967
1968	printf("SV[P]\tdelete last block, don't replace with padding\n");
1969	delete_from_our_metadata_(our_current_position--);
1970	if(!iterator.delete_block(/*replace_with_padding=*/false))
1971		return die_c_("iterator.delete_block(false)", chain.status());
1972
1973	block = iterator.get_block();
1974	if(!compare_chain_(chain, our_current_position, block))
1975		return false;
1976	delete block;
1977
1978	printf("S[V]\tprev\n");
1979	if(!iterator.prev())
1980		return die_("iterator ended early\n");
1981	our_current_position--;
1982
1983	printf("[S]V\tdelete STREAMINFO block, should fail\n");
1984	if(iterator.delete_block(/*replace_with_padding=*/false))
1985		return die_("iterator.delete_block() on STREAMINFO should have failed but didn't");
1986
1987	block = iterator.get_block();
1988	if(!compare_chain_(chain, our_current_position, block))
1989		return false;
1990	delete block;
1991
1992	} // delete iterator
1993	our_current_position = 0;
1994
1995	printf("SV\tmerge padding\n");
1996	chain.merge_padding();
1997
1998	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
1999		return die_c_("during chain.write(false, false)", chain.status());
2000	if(!compare_chain_(chain, 0, 0))
2001		return false;
2002	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
2003		return false;
2004
2005	printf("SV\tsort padding\n");
2006	chain.sort_padding();
2007
2008	if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg)))
2009		return die_c_("during chain.write(false, false)", chain.status());
2010	if(!compare_chain_(chain, 0, 0))
2011		return false;
2012	if(!test_file_(is_ogg, /*ignore_metadata=*/false))
2013		return false;
2014
2015end:
2016	if(!remove_file_(flacfilename(is_ogg)))
2017		return false;
2018
2019	return true;
2020}
2021
2022static bool test_level_2_misc_(bool is_ogg)
2023{
2024	::FLAC__IOCallbacks callbacks;
2025
2026	memset(&callbacks, 0, sizeof(callbacks));
2027	callbacks.read = (::FLAC__IOCallback_Read)fread;
2028#ifdef FLAC__VALGRIND_TESTING
2029	callbacks.write = chain_write_cb_;
2030#else
2031	callbacks.write = (::FLAC__IOCallback_Write)fwrite;
2032#endif
2033	callbacks.seek = chain_seek_cb_;
2034	callbacks.tell = chain_tell_cb_;
2035	callbacks.eof = chain_eof_cb_;
2036
2037	printf("\n\n++++++ testing level 2 interface (mismatched read/write protections)\n");
2038
2039	printf("generate file\n");
2040
2041	if(!generate_file_(/*include_extras=*/false, is_ogg))
2042		return false;
2043
2044	printf("create chain\n");
2045	FLAC::Metadata::Chain chain;
2046	if(!chain.is_valid())
2047		return die_("allocating chain");
2048
2049	printf("read chain (filename-based)\n");
2050
2051	if(!chain.read(flacfilename(is_ogg)))
2052		return die_c_("reading chain", chain.status());
2053
2054	printf("write chain with wrong method Chain::write(with callbacks)\n");
2055	{
2056		if(chain.write(/*use_padding=*/false, 0, callbacks))
2057			return die_c_("mismatched write should have failed", chain.status());
2058		if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2059			return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2060		printf("  OK: Chain::write(with callbacks) returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2061	}
2062
2063	printf("read chain (filename-based)\n");
2064
2065	if(!chain.read(flacfilename(is_ogg)))
2066		return die_c_("reading chain", chain.status());
2067
2068	printf("write chain with wrong method Chain::write(with callbacks and tempfile)\n");
2069	{
2070		if(chain.write(/*use_padding=*/false, 0, callbacks, 0, callbacks))
2071			return die_c_("mismatched write should have failed", chain.status());
2072		if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2073			return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2074		printf("  OK: Chain::write(with callbacks and tempfile) returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2075	}
2076
2077	printf("read chain (callback-based)\n");
2078	{
2079		FILE *file = fopen(flacfilename(is_ogg), "rb");
2080		if(0 == file)
2081			return die_("opening file");
2082		if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2083			fclose(file);
2084			return die_c_("reading chain", chain.status());
2085		}
2086		fclose(file);
2087	}
2088
2089	printf("write chain with wrong method write()\n");
2090	{
2091		if(chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
2092			return die_c_("mismatched write should have failed", chain.status());
2093		if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2094			return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2095		printf("  OK: write() returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2096	}
2097
2098	printf("read chain (callback-based)\n");
2099	{
2100		FILE *file = fopen(flacfilename(is_ogg), "rb");
2101		if(0 == file)
2102			return die_("opening file");
2103		if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2104			fclose(file);
2105			return die_c_("reading chain", chain.status());
2106		}
2107		fclose(file);
2108	}
2109
2110	printf("testing Chain::check_if_tempfile_needed()... ");
2111
2112	if(!chain.check_if_tempfile_needed(/*use_padding=*/false))
2113		printf("OK: Chain::check_if_tempfile_needed() returned false like it should\n");
2114	else
2115		return die_("Chain::check_if_tempfile_needed() returned true but shouldn't have");
2116
2117	printf("write chain with wrong method Chain::write(with callbacks and tempfile)\n");
2118	{
2119		if(chain.write(/*use_padding=*/false, 0, callbacks, 0, callbacks))
2120			return die_c_("mismatched write should have failed", chain.status());
2121		if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL)
2122			return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", chain.status());
2123		printf("  OK: Chain::write(with callbacks and tempfile) returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n");
2124	}
2125
2126	printf("read chain (callback-based)\n");
2127	{
2128		FILE *file = fopen(flacfilename(is_ogg), "rb");
2129		if(0 == file)
2130			return die_("opening file");
2131		if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2132			fclose(file);
2133			return die_c_("reading chain", chain.status());
2134		}
2135		fclose(file);
2136	}
2137
2138	printf("create iterator\n");
2139	{
2140	FLAC::Metadata::Iterator iterator;
2141	if(!iterator.is_valid())
2142		return die_("allocating memory for iterator");
2143
2144	iterator.init(chain);
2145
2146	printf("[S]VP\tnext\n");
2147	if(!iterator.next())
2148		return die_("iterator ended early\n");
2149
2150	printf("S[V]P\tdelete VORBIS_COMMENT, write\n");
2151	if(!iterator.delete_block(/*replace_with_padding=*/false))
2152		return die_c_("block delete failed\n", chain.status());
2153
2154	printf("testing Chain::check_if_tempfile_needed()... ");
2155
2156	if(chain.check_if_tempfile_needed(/*use_padding=*/false))
2157		printf("OK: Chain::check_if_tempfile_needed() returned true like it should\n");
2158	else
2159		return die_("Chain::check_if_tempfile_needed() returned false but shouldn't have");
2160
2161	printf("write chain with wrong method Chain::write(with callbacks)\n");
2162	{
2163		if(chain.write(/*use_padding=*/false, 0, callbacks))
2164			return die_c_("mismatched write should have failed", chain.status());
2165		if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL)
2166			return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", chain.status());
2167		printf("  OK: Chain::write(with callbacks) returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n");
2168	}
2169
2170	} // delete iterator
2171
2172	if(!remove_file_(flacfilename(is_ogg)))
2173		return false;
2174
2175	return true;
2176}
2177
2178bool test_metadata_file_manipulation()
2179{
2180	printf("\n+++ libFLAC++ unit test: metadata manipulation\n\n");
2181
2182	our_metadata_.num_blocks = 0;
2183
2184	if(!test_level_0_())
2185		return false;
2186
2187	if(!test_level_1_())
2188		return false;
2189
2190	if(!test_level_2_(/*filename_based=*/true, /*is_ogg=*/false)) /* filename-based */
2191		return false;
2192	if(!test_level_2_(/*filename_based=*/false, /*is_ogg=*/false)) /* callback-based */
2193		return false;
2194	if(!test_level_2_misc_(/*is_ogg=*/false))
2195		return false;
2196
2197	if(FLAC_API_SUPPORTS_OGG_FLAC) {
2198		if(!test_level_2_(/*filename_based=*/true, /*is_ogg=*/true)) /* filename-based */
2199			return false;
2200		if(!test_level_2_(/*filename_based=*/false, /*is_ogg=*/true)) /* callback-based */
2201			return false;
2202#if 0
2203		/* when ogg flac write is supported, will have to add this: */
2204		if(!test_level_2_misc_(/*is_ogg=*/true))
2205			return false;
2206#endif
2207	}
2208
2209	return true;
2210}
2211