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