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