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