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