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