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