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