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