add number-of-colors field to PICTURE metadata block
[platform/upstream/flac.git] / src / test_libFLAC++ / metadata_manip.cpp
1 /* test_libFLAC++ - Unit tester for libFLAC++
2  * Copyright (C) 2002,2003,2004,2005,2006  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_COLORS_LEN +
549                                 FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN /* will add the length for the data later */
550                         ) / 8
551                 ;
552                 picture.data.picture.type = ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER;
553                 picture.data.picture.mime_type = strdup_or_die_("image/jpeg");
554                 picture.length += strlen(picture.data.picture.mime_type);
555                 picture.data.picture.description = (FLAC__byte*)strdup_or_die_("desc");
556                 picture.length += strlen((const char *)picture.data.picture.description);
557                 picture.data.picture.width = 300;
558                 picture.data.picture.height = 300;
559                 picture.data.picture.depth = 24;
560                 picture.data.picture.colors = 0;
561                 picture.data.picture.data = (FLAC__byte*)strdup_or_die_("SOMEJPEGDATA");
562                 picture.data.picture.data_length = strlen((const char *)picture.data.picture.data);
563                 picture.length += picture.data.picture.data_length;
564         }
565
566         padding.is_last = true;
567         padding.type = ::FLAC__METADATA_TYPE_PADDING;
568         padding.length = 1234;
569
570         metadata[n++] = &vorbiscomment;
571         if(include_extras) {
572                 metadata[n++] = cuesheet;
573                 metadata[n++] = &picture;
574         }
575         metadata[n++] = &padding;
576
577         FLAC::Metadata::StreamInfo s(&streaminfo);
578         FLAC::Metadata::VorbisComment v(&vorbiscomment);
579         FLAC::Metadata::CueSheet c(cuesheet, /*copy=*/false);
580         FLAC::Metadata::Picture pi(&picture);
581         FLAC::Metadata::Padding p(&padding);
582         if(
583                 !insert_to_our_metadata_(&s, i++, /*copy=*/true) ||
584                 !insert_to_our_metadata_(&v, i++, /*copy=*/true) ||
585                 (include_extras && !insert_to_our_metadata_(&c, i++, /*copy=*/true)) ||
586                 (include_extras && !insert_to_our_metadata_(&pi, i++, /*copy=*/true)) ||
587                 !insert_to_our_metadata_(&p, i++, /*copy=*/true)
588         )
589                 return die_("priming our metadata");
590
591         if(!file_utils__generate_flacfile(flacfile_, 0, 512 * 1024, &streaminfo, metadata, n))
592                 return die_("creating the encoded file");
593
594         free(vorbiscomment.data.vorbis_comment.vendor_string.entry);
595
596         return true;
597 }
598
599 static bool test_file_(const char *filename, bool ignore_metadata)
600 {
601         OurFileDecoder decoder(ignore_metadata);
602
603         FLAC__ASSERT(0 != filename);
604
605         mc_our_block_number_ = 0;
606         decoder.error_occurred_ = false;
607
608         printf("\ttesting '%s'... ", filename);
609         fflush(stdout);
610
611         if(!decoder.is_valid())
612                 return die_("couldn't allocate decoder instance");
613
614         decoder.set_md5_checking(true);
615         decoder.set_metadata_respond_all();
616         if(decoder.init(filename) != ::FLAC__STREAM_DECODER_INIT_STATUS_OK) {
617                 decoder.finish();
618                 return die_("initializing decoder\n");
619         }
620         if(!decoder.process_until_end_of_stream()) {
621                 decoder.finish();
622                 return die_("decoding file\n");
623         }
624
625         decoder.finish();
626
627         if(decoder.error_occurred_)
628                 return false;
629
630         if(mc_our_block_number_ != our_metadata_.num_blocks)
631                 return die_("short metadata block count");
632
633         printf("PASSED\n");
634         return true;
635 }
636
637 static bool change_stats_(const char *filename, bool read_only)
638 {
639         if(!grabbag__file_change_stats(filename, read_only))
640                 return die_("during grabbag__file_change_stats()");
641
642         return true;
643 }
644
645 static bool remove_file_(const char *filename)
646 {
647         while(our_metadata_.num_blocks > 0)
648                 delete_from_our_metadata_(0);
649
650         if(!grabbag__file_remove_file(filename))
651                 return die_("removing file");
652
653         return true;
654 }
655
656 static bool test_level_0_()
657 {
658         FLAC::Metadata::StreamInfo streaminfo;
659
660         printf("\n\n++++++ testing level 0 interface\n");
661
662         if(!generate_file_(/*include_extras=*/true))
663                 return false;
664
665         if(!test_file_(flacfile_, /*ignore_metadata=*/true))
666                 return false;
667
668         printf("testing FLAC::Metadata::get_streaminfo()... ");
669
670         if(!FLAC::Metadata::get_streaminfo(flacfile_, streaminfo))
671                 return die_("during FLAC::Metadata::get_streaminfo()");
672
673         /* check to see if some basic data matches (c.f. generate_file_()) */
674         if(streaminfo.get_channels() != 1)
675                 return die_("mismatch in streaminfo.get_channels()");
676         if(streaminfo.get_bits_per_sample() != 8)
677                 return die_("mismatch in streaminfo.get_bits_per_sample()");
678         if(streaminfo.get_sample_rate() != 44100)
679                 return die_("mismatch in streaminfo.get_sample_rate()");
680         if(streaminfo.get_min_blocksize() != 576)
681                 return die_("mismatch in streaminfo.get_min_blocksize()");
682         if(streaminfo.get_max_blocksize() != 576)
683                 return die_("mismatch in streaminfo.get_max_blocksize()");
684
685         printf("OK\n");
686
687         {
688                 printf("testing FLAC::Metadata::get_tags(VorbisComment *&)... ");
689
690                 FLAC::Metadata::VorbisComment *tags = 0;
691
692                 if(!FLAC::Metadata::get_tags(flacfile_, tags))
693                         return die_("during FLAC::Metadata::get_tags()");
694
695                 /* check to see if some basic data matches (c.f. generate_file_()) */
696                 if(tags->get_num_comments() != 0)
697                         return die_("mismatch in tags->get_num_comments()");
698
699                 printf("OK\n");
700
701                 delete tags;
702         }
703
704         {
705                 printf("testing FLAC::Metadata::get_tags(VorbisComment &)... ");
706
707                 FLAC::Metadata::VorbisComment tags;
708
709                 if(!FLAC::Metadata::get_tags(flacfile_, tags))
710                         return die_("during FLAC::Metadata::get_tags()");
711
712                 /* check to see if some basic data matches (c.f. generate_file_()) */
713                 if(tags.get_num_comments() != 0)
714                         return die_("mismatch in tags.get_num_comments()");
715
716                 printf("OK\n");
717         }
718
719         {
720                 printf("testing FLAC::Metadata::get_cuesheet(CueSheet *&)... ");
721
722                 FLAC::Metadata::CueSheet *cuesheet = 0;
723
724                 if(!FLAC::Metadata::get_cuesheet(flacfile_, cuesheet))
725                         return die_("during FLAC::Metadata::get_cuesheet()");
726
727                 /* check to see if some basic data matches (c.f. generate_file_()) */
728                 if(cuesheet->get_lead_in() != 123)
729                         return die_("mismatch in cuesheet->get_lead_in()");
730
731                 printf("OK\n");
732
733                 delete cuesheet;
734         }
735
736         {
737                 printf("testing FLAC::Metadata::get_cuesheet(CueSheet &)... ");
738
739                 FLAC::Metadata::CueSheet cuesheet;
740
741                 if(!FLAC::Metadata::get_cuesheet(flacfile_, cuesheet))
742                         return die_("during FLAC::Metadata::get_cuesheet()");
743
744                 /* check to see if some basic data matches (c.f. generate_file_()) */
745                 if(cuesheet.get_lead_in() != 123)
746                         return die_("mismatch in cuesheet.get_lead_in()");
747
748                 printf("OK\n");
749         }
750
751         {
752                 printf("testing FLAC::Metadata::get_picture(Picture *&)... ");
753
754                 FLAC::Metadata::Picture *picture = 0;
755
756                 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), /*max_colors=*/(unsigned)(-1)))
757                         return die_("during FLAC::Metadata::get_picture()");
758
759                 /* check to see if some basic data matches (c.f. generate_file_()) */
760                 if(picture->get_type () != ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
761                         return die_("mismatch in picture->get_type ()");
762
763                 printf("OK\n");
764
765                 delete picture;
766         }
767
768         {
769                 printf("testing FLAC::Metadata::get_picture(Picture &)... ");
770
771                 FLAC::Metadata::Picture picture;
772
773                 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), /*max_colors=*/(unsigned)(-1)))
774                         return die_("during FLAC::Metadata::get_picture()");
775
776                 /* check to see if some basic data matches (c.f. generate_file_()) */
777                 if(picture.get_type () != ::FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
778                         return die_("mismatch in picture->get_type ()");
779
780                 printf("OK\n");
781         }
782
783         if(!remove_file_(flacfile_))
784                 return false;
785
786         return true;
787 }
788
789 static bool test_level_1_()
790 {
791         FLAC::Metadata::Prototype *block;
792         FLAC::Metadata::StreamInfo *streaminfo;
793         FLAC::Metadata::Padding *padding;
794         FLAC::Metadata::Application *app;
795         FLAC__byte data[1000];
796         unsigned our_current_position = 0;
797
798         // initialize 'data' to avoid Valgrind errors
799         memset(data, 0, sizeof(data));
800
801         printf("\n\n++++++ testing level 1 interface\n");
802
803         /************************************************************/
804         {
805         printf("simple iterator on read-only file\n");
806
807         if(!generate_file_(/*include_extras=*/false))
808                 return false;
809
810         if(!change_stats_(flacfile_, /*read_only=*/true))
811                 return false;
812
813         if(!test_file_(flacfile_, /*ignore_metadata=*/true))
814                 return false;
815
816         FLAC::Metadata::SimpleIterator iterator;
817
818         if(!iterator.is_valid())
819                 return die_("iterator.is_valid() returned false");
820
821         if(!iterator.init(flacfile_, /*read_only=*/false, /*preserve_file_stats=*/false))
822                 return die_("iterator.init() returned false");
823
824         printf("is writable = %u\n", (unsigned)iterator.is_writable());
825         if(iterator.is_writable())
826                 return die_("iterator claims file is writable when tester thinks it should not be; are you running as root?\n");
827
828         printf("iterate forwards\n");
829
830         if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
831                 return die_("expected STREAMINFO type from iterator.get_block_type()");
832         if(0 == (block = iterator.get_block()))
833                 return die_("getting block 0");
834         if(block->get_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
835                 return die_("expected STREAMINFO type");
836         if(block->get_is_last())
837                 return die_("expected is_last to be false");
838         if(block->get_length() != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
839                 return die_("bad STREAMINFO length");
840         /* check to see if some basic data matches (c.f. generate_file_()) */
841         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
842         FLAC__ASSERT(0 != streaminfo);
843         if(streaminfo->get_channels() != 1)
844                 return die_("mismatch in channels");
845         if(streaminfo->get_bits_per_sample() != 8)
846                 return die_("mismatch in bits_per_sample");
847         if(streaminfo->get_sample_rate() != 44100)
848                 return die_("mismatch in sample_rate");
849         if(streaminfo->get_min_blocksize() != 576)
850                 return die_("mismatch in min_blocksize");
851         if(streaminfo->get_max_blocksize() != 576)
852                 return die_("mismatch in max_blocksize");
853         // we will delete streaminfo a little later when we're really done with it...
854
855         if(!iterator.next())
856                 return die_("forward iterator ended early");
857         our_current_position++;
858
859         if(!iterator.next())
860                 return die_("forward iterator ended early");
861         our_current_position++;
862
863         if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_PADDING)
864                 return die_("expected PADDING type from iterator.get_block_type()");
865         if(0 == (block = iterator.get_block()))
866                 return die_("getting block 1");
867         if(block->get_type() != ::FLAC__METADATA_TYPE_PADDING)
868                 return die_("expected PADDING type");
869         if(!block->get_is_last())
870                 return die_("expected is_last to be true");
871         /* check to see if some basic data matches (c.f. generate_file_()) */
872         if(block->get_length() != 1234)
873                 return die_("bad PADDING length");
874         delete block;
875
876         if(iterator.next())
877                 return die_("forward iterator returned true but should have returned false");
878
879         printf("iterate backwards\n");
880         if(!iterator.prev())
881                 return die_("reverse iterator ended early");
882         if(!iterator.prev())
883                 return die_("reverse iterator ended early");
884         if(iterator.prev())
885                 return die_("reverse iterator returned true but should have returned false");
886
887         printf("testing iterator.set_block() on read-only file...\n");
888
889         if(!iterator.set_block(streaminfo, false))
890                 printf("PASSED.  iterator.set_block() returned false like it should\n");
891         else
892                 return die_("iterator.set_block() returned true but shouldn't have");
893         delete streaminfo;
894         }
895
896         /************************************************************/
897         {
898         printf("simple iterator on writable file\n");
899
900         if(!change_stats_(flacfile_, /*read-only=*/false))
901                 return false;
902
903         printf("creating APPLICATION block\n");
904
905         if(0 == (app = new FLAC::Metadata::Application()))
906                 return die_("new FLAC::Metadata::Application()");
907         app->set_id((const unsigned char *)"duh");
908
909         printf("creating PADDING block\n");
910
911         if(0 == (padding = new FLAC::Metadata::Padding()))
912                 return die_("new FLAC::Metadata::Padding()");
913         padding->set_length(20);
914
915         FLAC::Metadata::SimpleIterator iterator;
916
917         if(!iterator.is_valid())
918                 return die_("iterator.is_valid() returned false");
919
920         if(!iterator.init(flacfile_, /*read_only=*/false, /*preserve_file_stats=*/false))
921                 return die_("iterator.init() returned false");
922         our_current_position = 0;
923
924         printf("is writable = %u\n", (unsigned)iterator.is_writable());
925
926         printf("[S]VP\ttry to write over STREAMINFO block...\n");
927         if(!iterator.set_block(app, false))
928                 printf("\titerator.set_block() returned false like it should\n");
929         else
930                 return die_("iterator.set_block() returned true but shouldn't have");
931
932         printf("[S]VP\tnext\n");
933         if(!iterator.next())
934                 return die_("iterator ended early\n");
935         our_current_position++;
936
937         printf("S[V]P\tnext\n");
938         if(!iterator.next())
939                 return die_("iterator ended early\n");
940         our_current_position++;
941
942         printf("SV[P]\tinsert PADDING after, don't expand into padding\n");
943         padding->set_length(25);
944         if(!iterator.insert_block_after(padding, false))
945                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
946         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
947                 return false;
948
949         printf("SVP[P]\tprev\n");
950         if(!iterator.prev())
951                 return die_("iterator ended early\n");
952         our_current_position--;
953
954         printf("SV[P]P\tprev\n");
955         if(!iterator.prev())
956                 return die_("iterator ended early\n");
957         our_current_position--;
958
959         printf("S[V]PP\tinsert PADDING after, don't expand into padding\n");
960         padding->set_length(30);
961         if(!iterator.insert_block_after(padding, false))
962                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
963         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
964                 return false;
965
966         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
967                 return false;
968
969         printf("SV[P]PP\tprev\n");
970         if(!iterator.prev())
971                 return die_("iterator ended early\n");
972         our_current_position--;
973
974         printf("S[V]PPP\tprev\n");
975         if(!iterator.prev())
976                 return die_("iterator ended early\n");
977         our_current_position--;
978
979         printf("[S]VPPP\tdelete (STREAMINFO block), must fail\n");
980         if(iterator.delete_block(false))
981                 return die_ss_("iterator.delete_block(false) should have returned false", iterator);
982
983         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
984                 return false;
985
986         printf("[S]VPPP\tnext\n");
987         if(!iterator.next())
988                 return die_("iterator ended early\n");
989         our_current_position++;
990
991         printf("S[V]PPP\tnext\n");
992         if(!iterator.next())
993                 return die_("iterator ended early\n");
994         our_current_position++;
995
996         printf("SV[P]PP\tdelete (middle block), replace with padding\n");
997         if(!iterator.delete_block(true))
998                 return die_ss_("iterator.delete_block(true)", iterator);
999         our_current_position--;
1000
1001         printf("S[V]PPP\tnext\n");
1002         if(!iterator.next())
1003                 return die_("iterator ended early\n");
1004         our_current_position++;
1005
1006         printf("SV[P]PP\tdelete (middle block), don't replace with padding\n");
1007         if(!iterator.delete_block(false))
1008                 return die_ss_("iterator.delete_block(false)", iterator);
1009         delete_from_our_metadata_(our_current_position--);
1010
1011         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1012                 return false;
1013
1014         printf("S[V]PP\tnext\n");
1015         if(!iterator.next())
1016                 return die_("iterator ended early\n");
1017         our_current_position++;
1018
1019         printf("SV[P]P\tnext\n");
1020         if(!iterator.next())
1021                 return die_("iterator ended early\n");
1022         our_current_position++;
1023
1024         printf("SVP[P]\tdelete (last block), replace with padding\n");
1025         if(!iterator.delete_block(true))
1026                 return die_ss_("iterator.delete_block(false)", iterator);
1027         our_current_position--;
1028
1029         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1030                 return false;
1031
1032         printf("SV[P]P\tnext\n");
1033         if(!iterator.next())
1034                 return die_("iterator ended early\n");
1035         our_current_position++;
1036
1037         printf("SVP[P]\tdelete (last block), don't replace with padding\n");
1038         if(!iterator.delete_block(false))
1039                 return die_ss_("iterator.delete_block(false)", iterator);
1040         delete_from_our_metadata_(our_current_position--);
1041
1042         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1043                 return false;
1044
1045         printf("SV[P]\tprev\n");
1046         if(!iterator.prev())
1047                 return die_("iterator ended early\n");
1048         our_current_position--;
1049
1050         printf("S[V]P\tprev\n");
1051         if(!iterator.prev())
1052                 return die_("iterator ended early\n");
1053         our_current_position--;
1054
1055         printf("[S]VP\tset STREAMINFO (change sample rate)\n");
1056         FLAC__ASSERT(our_current_position == 0);
1057         block = iterator.get_block();
1058         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
1059         FLAC__ASSERT(0 != streaminfo);
1060         streaminfo->set_sample_rate(32000);
1061         if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
1062                 return die_("copying object");
1063         if(!iterator.set_block(block, false))
1064                 return die_ss_("iterator.set_block(block, false)", iterator);
1065         delete block;
1066
1067         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1068                 return false;
1069
1070         printf("[S]VP\tnext\n");
1071         if(!iterator.next())
1072                 return die_("iterator ended early\n");
1073         our_current_position++;
1074
1075         printf("S[V]P\tinsert APPLICATION after, expand into padding of exceeding size\n");
1076         app->set_id((const unsigned char *)"euh"); /* twiddle the id so that our comparison doesn't miss transposition */
1077         if(!iterator.insert_block_after(app, true))
1078                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1079         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1080                 return false;
1081         add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
1082
1083         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1084                 return false;
1085
1086         printf("SV[A]P\tnext\n");
1087         if(!iterator.next())
1088                 return die_("iterator ended early\n");
1089         our_current_position++;
1090
1091         printf("SVA[P]\tset APPLICATION, expand into padding of exceeding size\n");
1092         app->set_id((const unsigned char *)"fuh"); /* twiddle the id */
1093         if(!iterator.set_block(app, true))
1094                 return die_ss_("iterator.set_block(app, true)", iterator);
1095         if(!insert_to_our_metadata_(app, our_current_position, /*copy=*/true))
1096                 return false;
1097         add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
1098
1099         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1100                 return false;
1101
1102         printf("SVA[A]P\tset APPLICATION (grow), don't expand into padding\n");
1103         app->set_id((const unsigned char *)"guh"); /* twiddle the id */
1104         if(!app->set_data(data, sizeof(data), true))
1105                 return die_("setting APPLICATION data");
1106         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1107                 return die_("copying object");
1108         if(!iterator.set_block(app, false))
1109                 return die_ss_("iterator.set_block(app, false)", iterator);
1110
1111         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1112                 return false;
1113
1114         printf("SVA[A]P\tset APPLICATION (shrink), don't fill in with padding\n");
1115         app->set_id((const unsigned char *)"huh"); /* twiddle the id */
1116         if(!app->set_data(data, 12, true))
1117                 return die_("setting APPLICATION data");
1118         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1119                 return die_("copying object");
1120         if(!iterator.set_block(app, false))
1121                 return die_ss_("iterator.set_block(app, false)", iterator);
1122
1123         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1124                 return false;
1125
1126         printf("SVA[A]P\tset APPLICATION (grow), expand into padding of exceeding size\n");
1127         app->set_id((const unsigned char *)"iuh"); /* twiddle the id */
1128         if(!app->set_data(data, sizeof(data), true))
1129                 return die_("setting APPLICATION data");
1130         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1131                 return die_("copying object");
1132         add_to_padding_length_(our_current_position+1, -((int)sizeof(data) - 12));
1133         if(!iterator.set_block(app, true))
1134                 return die_ss_("iterator.set_block(app, true)", iterator);
1135
1136         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1137                 return false;
1138
1139         printf("SVA[A]P\tset APPLICATION (shrink), fill in with padding\n");
1140         app->set_id((const unsigned char *)"juh"); /* twiddle the id */
1141         if(!app->set_data(data, 23, true))
1142                 return die_("setting APPLICATION data");
1143         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1144                 return die_("copying object");
1145         if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/true))
1146                 return die_("copying object");
1147         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(sizeof(data) - 23 - FLAC__STREAM_METADATA_HEADER_LENGTH);
1148         if(!iterator.set_block(app, true))
1149                 return die_ss_("iterator.set_block(app, true)", iterator);
1150
1151         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1152                 return false;
1153
1154         printf("SVA[A]PP\tnext\n");
1155         if(!iterator.next())
1156                 return die_("iterator ended early\n");
1157         our_current_position++;
1158
1159         printf("SVAA[P]P\tnext\n");
1160         if(!iterator.next())
1161                 return die_("iterator ended early\n");
1162         our_current_position++;
1163
1164         printf("SVAAP[P]\tset PADDING (shrink), don't fill in with padding\n");
1165         padding->set_length(5);
1166         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1167                 return die_("copying object");
1168         if(!iterator.set_block(padding, false))
1169                 return die_ss_("iterator.set_block(padding, false)", iterator);
1170
1171         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1172                 return false;
1173
1174         printf("SVAAP[P]\tset APPLICATION (grow)\n");
1175         app->set_id((const unsigned char *)"kuh"); /* twiddle the id */
1176         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1177                 return die_("copying object");
1178         if(!iterator.set_block(app, false))
1179                 return die_ss_("iterator.set_block(app, false)", iterator);
1180
1181         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1182                 return false;
1183
1184         printf("SVAAP[A]\tset PADDING (equal)\n");
1185         padding->set_length(27);
1186         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1187                 return die_("copying object");
1188         if(!iterator.set_block(padding, false))
1189                 return die_ss_("iterator.set_block(padding, false)", iterator);
1190
1191         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1192                 return false;
1193
1194         printf("SVAAP[P]\tprev\n");
1195         if(!iterator.prev())
1196                 return die_("iterator ended early\n");
1197         our_current_position--;
1198
1199         printf("SVAA[P]P\tdelete (middle block), don't replace with padding\n");
1200         if(!iterator.delete_block(false))
1201                 return die_ss_("iterator.delete_block(false)", iterator);
1202         delete_from_our_metadata_(our_current_position--);
1203
1204         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1205                 return false;
1206
1207         printf("SVA[A]P\tdelete (middle block), don't replace with padding\n");
1208         if(!iterator.delete_block(false))
1209                 return die_ss_("iterator.delete_block(false)", iterator);
1210         delete_from_our_metadata_(our_current_position--);
1211
1212         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1213                 return false;
1214
1215         printf("SV[A]P\tnext\n");
1216         if(!iterator.next())
1217                 return die_("iterator ended early\n");
1218         our_current_position++;
1219
1220         printf("SVA[P]\tinsert PADDING after\n");
1221         padding->set_length(5);
1222         if(!iterator.insert_block_after(padding, false))
1223                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1224         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1225                 return false;
1226
1227         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1228                 return false;
1229
1230         printf("SVAP[P]\tprev\n");
1231         if(!iterator.prev())
1232                 return die_("iterator ended early\n");
1233         our_current_position--;
1234
1235         printf("SVA[P]P\tprev\n");
1236         if(!iterator.prev())
1237                 return die_("iterator ended early\n");
1238         our_current_position--;
1239
1240         printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is too small\n");
1241         if(!app->set_data(data, 32, true))
1242                 return die_("setting APPLICATION data");
1243         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1244                 return die_("copying object");
1245         if(!iterator.set_block(app, true))
1246                 return die_ss_("iterator.set_block(app, true)", iterator);
1247
1248         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1249                 return false;
1250
1251         printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is 'close' but still too small\n");
1252         if(!app->set_data(data, 60, true))
1253                 return die_("setting APPLICATION data");
1254         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1255                 return die_("copying object");
1256         if(!iterator.set_block(app, true))
1257                 return die_ss_("iterator.set_block(app, true)", iterator);
1258
1259         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1260                 return false;
1261
1262         printf("SV[A]PP\tset APPLICATION (grow), expand into padding which will leave 0-length pad\n");
1263         if(!app->set_data(data, 87, true))
1264                 return die_("setting APPLICATION data");
1265         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1266                 return die_("copying object");
1267         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
1268         if(!iterator.set_block(app, true))
1269                 return die_ss_("iterator.set_block(app, true)", iterator);
1270
1271         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1272                 return false;
1273
1274         printf("SV[A]PP\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
1275         if(!app->set_data(data, 91, true))
1276                 return die_("setting APPLICATION data");
1277         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1278                 return die_("copying object");
1279         delete_from_our_metadata_(our_current_position+1);
1280         if(!iterator.set_block(app, true))
1281                 return die_ss_("iterator.set_block(app, true)", iterator);
1282
1283         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1284                 return false;
1285
1286         printf("SV[A]P\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
1287         if(!app->set_data(data, 100, true))
1288                 return die_("setting APPLICATION data");
1289         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1290                 return die_("copying object");
1291         delete_from_our_metadata_(our_current_position+1);
1292         our_metadata_.blocks[our_current_position]->set_is_last(true);
1293         if(!iterator.set_block(app, true))
1294                 return die_ss_("iterator.set_block(app, true)", iterator);
1295
1296         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1297                 return false;
1298
1299         printf("SV[A]\tset PADDING (equal size)\n");
1300         padding->set_length(app->get_length());
1301         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
1302                 return die_("copying object");
1303         if(!iterator.set_block(padding, true))
1304                 return die_ss_("iterator.set_block(padding, true)", iterator);
1305
1306         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1307                 return false;
1308
1309         printf("SV[P]\tinsert PADDING after\n");
1310         if(!iterator.insert_block_after(padding, false))
1311                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1312         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1313                 return false;
1314
1315         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1316                 return false;
1317
1318         printf("SVP[P]\tinsert PADDING after\n");
1319         padding->set_length(5);
1320         if(!iterator.insert_block_after(padding, false))
1321                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
1322         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1323                 return false;
1324
1325         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1326                 return false;
1327
1328         printf("SVPP[P]\tprev\n");
1329         if(!iterator.prev())
1330                 return die_("iterator ended early\n");
1331         our_current_position--;
1332
1333         printf("SVP[P]P\tprev\n");
1334         if(!iterator.prev())
1335                 return die_("iterator ended early\n");
1336         our_current_position--;
1337
1338         printf("SV[P]PP\tprev\n");
1339         if(!iterator.prev())
1340                 return die_("iterator ended early\n");
1341         our_current_position--;
1342
1343         printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is too small\n");
1344         if(!app->set_data(data, 101, true))
1345                 return die_("setting APPLICATION data");
1346         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1347                 return die_("copying object");
1348         if(!iterator.insert_block_after(app, true))
1349                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1350
1351         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1352                 return false;
1353
1354         printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
1355         if(!iterator.delete_block(false))
1356                 return die_ss_("iterator.delete_block(false)", iterator);
1357         delete_from_our_metadata_(our_current_position--);
1358
1359         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1360                 return false;
1361
1362         printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is 'close' but still too small\n");
1363         if(!app->set_data(data, 97, true))
1364                 return die_("setting APPLICATION data");
1365         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1366                 return die_("copying object");
1367         if(!iterator.insert_block_after(app, true))
1368                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1369
1370         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1371                 return false;
1372
1373         printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
1374         if(!iterator.delete_block(false))
1375                 return die_ss_("iterator.delete_block(false)", iterator);
1376         delete_from_our_metadata_(our_current_position--);
1377
1378         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1379                 return false;
1380
1381         printf("S[V]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1382         if(!app->set_data(data, 100, true))
1383                 return die_("setting APPLICATION data");
1384         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1385                 return die_("copying object");
1386         delete_from_our_metadata_(our_current_position+1);
1387         if(!iterator.insert_block_after(app, true))
1388                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1389
1390         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1391                 return false;
1392
1393         printf("SV[A]PP\tdelete (middle block), don't replace with padding\n");
1394         if(!iterator.delete_block(false))
1395                 return die_ss_("iterator.delete_block(false)", iterator);
1396         delete_from_our_metadata_(our_current_position--);
1397
1398         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1399                 return false;
1400
1401         printf("S[V]PP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n");
1402         if(!app->set_data(data, 96, true))
1403                 return die_("setting APPLICATION data");
1404         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1405                 return die_("copying object");
1406         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
1407         if(!iterator.insert_block_after(app, true))
1408                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1409
1410         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1411                 return false;
1412
1413         printf("SV[A]PP\tdelete (middle block), don't replace with padding\n");
1414         if(!iterator.delete_block(false))
1415                 return die_ss_("iterator.delete_block(false)", iterator);
1416         delete_from_our_metadata_(our_current_position--);
1417
1418         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1419                 return false;
1420
1421         printf("S[V]PP\tnext\n");
1422         if(!iterator.next())
1423                 return die_("iterator ended early\n");
1424         our_current_position++;
1425
1426         printf("SV[P]P\tdelete (middle block), don't replace with padding\n");
1427         if(!iterator.delete_block(false))
1428                 return die_ss_("iterator.delete_block(false)", iterator);
1429         delete_from_our_metadata_(our_current_position--);
1430
1431         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1432                 return false;
1433
1434         printf("S[V]P\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1435         if(!app->set_data(data, 1, true))
1436                 return die_("setting APPLICATION data");
1437         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1438                 return die_("copying object");
1439         delete_from_our_metadata_(our_current_position+1);
1440         if(!iterator.insert_block_after(app, true))
1441                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1442
1443         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1444                 return false;
1445         }
1446
1447         delete app;
1448         delete padding;
1449
1450         if(!remove_file_(flacfile_))
1451                 return false;
1452
1453         return true;
1454 }
1455
1456 static bool test_level_2_(bool filename_based)
1457 {
1458         FLAC::Metadata::Prototype *block;
1459         FLAC::Metadata::StreamInfo *streaminfo;
1460         FLAC::Metadata::Application *app;
1461         FLAC::Metadata::Padding *padding;
1462         FLAC__byte data[2000];
1463         unsigned our_current_position;
1464
1465         // initialize 'data' to avoid Valgrind errors
1466         memset(data, 0, sizeof(data));
1467
1468         printf("\n\n++++++ testing level 2 interface (%s-based)\n", filename_based? "filename":"callback");
1469
1470         printf("generate read-only file\n");
1471
1472         if(!generate_file_(/*include_extras=*/false))
1473                 return false;
1474
1475         if(!change_stats_(flacfile_, /*read_only=*/true))
1476                 return false;
1477
1478         printf("create chain\n");
1479         FLAC::Metadata::Chain chain;
1480         if(!chain.is_valid())
1481                 return die_("allocating memory for chain");
1482
1483         printf("read chain\n");
1484
1485         if(!read_chain_(chain, flacfile_, filename_based))
1486                 return die_c_("reading chain", chain.status());
1487
1488         printf("[S]VP\ttest initial metadata\n");
1489
1490         if(!compare_chain_(chain, 0, 0))
1491                 return false;
1492         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1493                 return false;
1494
1495         printf("switch file to read-write\n");
1496
1497         if(!change_stats_(flacfile_, /*read-only=*/false))
1498                 return false;
1499
1500         printf("create iterator\n");
1501         {
1502         FLAC::Metadata::Iterator iterator;
1503         if(!iterator.is_valid())
1504                 return die_("allocating memory for iterator");
1505
1506         our_current_position = 0;
1507
1508         iterator.init(chain);
1509
1510         if(0 == (block = iterator.get_block()))
1511                 return die_("getting block from iterator");
1512
1513         FLAC__ASSERT(block->get_type() == FLAC__METADATA_TYPE_STREAMINFO);
1514
1515         printf("[S]VP\tmodify STREAMINFO, write\n");
1516
1517         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
1518         FLAC__ASSERT(0 != streaminfo);
1519         streaminfo->set_sample_rate(32000);
1520         if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
1521                 return die_("copying object");
1522         delete block;
1523
1524         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/true, filename_based, flacfile_))
1525                 return die_c_("during chain.write(false, true)", chain.status());
1526         block = iterator.get_block();
1527         if(!compare_chain_(chain, our_current_position, block))
1528                 return false;
1529         delete block;
1530         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1531                 return false;
1532
1533         printf("[S]VP\tnext\n");
1534         if(!iterator.next())
1535                 return die_("iterator ended early\n");
1536         our_current_position++;
1537
1538         printf("S[V]P\tnext\n");
1539         if(!iterator.next())
1540                 return die_("iterator ended early\n");
1541         our_current_position++;
1542
1543         printf("SV[P]\treplace PADDING with identical-size APPLICATION\n");
1544         if(0 == (block = iterator.get_block()))
1545                 return die_("getting block from iterator");
1546         if(0 == (app = new FLAC::Metadata::Application()))
1547                 return die_("new FLAC::Metadata::Application()");
1548         app->set_id((const unsigned char *)"duh");
1549         if(!app->set_data(data, block->get_length()-(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), true))
1550                 return die_("setting APPLICATION data");
1551         delete block;
1552         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1553                 return die_("copying object");
1554         if(!iterator.set_block(app))
1555                 return die_c_("iterator.set_block(app)", chain.status());
1556
1557         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1558                 return die_c_("during chain.write(false, false)", chain.status());
1559         block = iterator.get_block();
1560         if(!compare_chain_(chain, our_current_position, block))
1561                 return false;
1562         delete block;
1563         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1564                 return false;
1565
1566         printf("SV[A]\tshrink APPLICATION, don't use padding\n");
1567         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1568                 return die_("copying object");
1569         if(!app->set_data(data, 26, true))
1570                 return die_("setting APPLICATION data");
1571         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1572                 return die_("copying object");
1573         if(!iterator.set_block(app))
1574                 return die_c_("iterator.set_block(app)", chain.status());
1575
1576         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1577                 return die_c_("during chain.write(false, false)", chain.status());
1578         block = iterator.get_block();
1579         if(!compare_chain_(chain, our_current_position, block))
1580                 return false;
1581         delete block;
1582         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1583                 return false;
1584
1585         printf("SV[A]\tgrow APPLICATION, don't use padding\n");
1586         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1587                 return die_("copying object");
1588         if(!app->set_data(data, 28, true))
1589                 return die_("setting APPLICATION data");
1590         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1591                 return die_("copying object");
1592         if(!iterator.set_block(app))
1593                 return die_c_("iterator.set_block(app)", chain.status());
1594
1595         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1596                 return die_c_("during chain.write(false, false)", chain.status());
1597         block = iterator.get_block();
1598         if(!compare_chain_(chain, our_current_position, block))
1599                 return false;
1600         delete block;
1601         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1602                 return false;
1603
1604         printf("SV[A]\tgrow APPLICATION, use padding, but last block is not padding\n");
1605         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1606                 return die_("copying object");
1607         if(!app->set_data(data, 36, true))
1608                 return die_("setting APPLICATION data");
1609         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1610                 return die_("copying object");
1611         if(!iterator.set_block(app))
1612                 return die_c_("iterator.set_block(app)", chain.status());
1613
1614         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1615                 return die_c_("during chain.write(false, false)", chain.status());
1616         block = iterator.get_block();
1617         if(!compare_chain_(chain, our_current_position, block))
1618                 return false;
1619         delete block;
1620         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1621                 return false;
1622
1623         printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, but delta is too small for new PADDING block\n");
1624         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1625                 return die_("copying object");
1626         if(!app->set_data(data, 33, true))
1627                 return die_("setting APPLICATION data");
1628         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1629                 return die_("copying object");
1630         if(!iterator.set_block(app))
1631                 return die_c_("iterator.set_block(app)", chain.status());
1632
1633         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1634                 return die_c_("during chain.write(true, false)", chain.status());
1635         block = iterator.get_block();
1636         if(!compare_chain_(chain, our_current_position, block))
1637                 return false;
1638         delete block;
1639         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1640                 return false;
1641
1642         printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, delta is enough for new PADDING block\n");
1643         if(0 == (padding = new FLAC::Metadata::Padding()))
1644                 return die_("creating PADDING block");
1645         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1646                 return die_("copying object");
1647         if(!app->set_data(data, 29, true))
1648                 return die_("setting APPLICATION data");
1649         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1650                 return die_("copying object");
1651         padding->set_length(0);
1652         if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/false))
1653                 return die_("internal error");
1654         if(!iterator.set_block(app))
1655                 return die_c_("iterator.set_block(app)", chain.status());
1656
1657         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1658                 return die_c_("during chain.write(true, false)", chain.status());
1659         block = iterator.get_block();
1660         if(!compare_chain_(chain, our_current_position, block))
1661                 return false;
1662         delete block;
1663         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1664                 return false;
1665
1666         printf("SV[A]P\tshrink APPLICATION, use padding, last block is padding\n");
1667         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1668                 return die_("copying object");
1669         if(!app->set_data(data, 16, true))
1670                 return die_("setting APPLICATION data");
1671         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1672                 return die_("copying object");
1673         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(13);
1674         if(!iterator.set_block(app))
1675                 return die_c_("iterator.set_block(app)", chain.status());
1676
1677         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1678                 return die_c_("during chain.write(true, false)", chain.status());
1679         block = iterator.get_block();
1680         if(!compare_chain_(chain, our_current_position, block))
1681                 return false;
1682         delete block;
1683         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1684                 return false;
1685
1686         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding, but delta is too small\n");
1687         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1688                 return die_("copying object");
1689         if(!app->set_data(data, 50, true))
1690                 return die_("setting APPLICATION data");
1691         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1692                 return die_("copying object");
1693         if(!iterator.set_block(app))
1694                 return die_c_("iterator.set_block(app)", chain.status());
1695
1696         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1697                 return die_c_("during chain.write(true, false)", chain.status());
1698         block = iterator.get_block();
1699         if(!compare_chain_(chain, our_current_position, block))
1700                 return false;
1701         delete block;
1702         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1703                 return false;
1704
1705         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exceeding size\n");
1706         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1707                 return die_("copying object");
1708         if(!app->set_data(data, 56, true))
1709                 return die_("setting APPLICATION data");
1710         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1711                 return die_("copying object");
1712         add_to_padding_length_(our_current_position+1, -(56 - 50));
1713         if(!iterator.set_block(app))
1714                 return die_c_("iterator.set_block(app)", chain.status());
1715
1716         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1717                 return die_c_("during chain.write(true, false)", chain.status());
1718         block = iterator.get_block();
1719         if(!compare_chain_(chain, our_current_position, block))
1720                 return false;
1721         delete block;
1722         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1723                 return false;
1724
1725         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exact size\n");
1726         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1727                 return die_("copying object");
1728         if(!app->set_data(data, 67, true))
1729                 return die_("setting APPLICATION data");
1730         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1731                 return die_("copying object");
1732         delete_from_our_metadata_(our_current_position+1);
1733         if(!iterator.set_block(app))
1734                 return die_c_("iterator.set_block(app)", chain.status());
1735
1736         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1737                 return die_c_("during chain.write(true, false)", chain.status());
1738         block = iterator.get_block();
1739         if(!compare_chain_(chain, our_current_position, block))
1740                 return false;
1741         delete block;
1742         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1743                 return false;
1744
1745         printf("SV[A]\tprev\n");
1746         if(!iterator.prev())
1747                 return die_("iterator ended early\n");
1748         our_current_position--;
1749
1750         printf("S[V]A\tprev\n");
1751         if(!iterator.prev())
1752                 return die_("iterator ended early\n");
1753         our_current_position--;
1754
1755         printf("[S]VA\tinsert PADDING before STREAMINFO (should fail)\n");
1756         if(0 == (padding = new FLAC::Metadata::Padding()))
1757                 return die_("creating PADDING block");
1758         padding->set_length(30);
1759         if(!iterator.insert_block_before(padding))
1760                 printf("\titerator.insert_block_before() returned false like it should\n");
1761         else
1762                 return die_("iterator.insert_block_before() should have returned false");
1763
1764         printf("[S]VA\tnext\n");
1765         if(!iterator.next())
1766                 return die_("iterator ended early\n");
1767         our_current_position++;
1768
1769         printf("S[V]A\tinsert PADDING after\n");
1770         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1771                 return die_("copying metadata");
1772         if(!iterator.insert_block_after(padding))
1773                 return die_("iterator.insert_block_after(padding)");
1774
1775         block = iterator.get_block();
1776         if(!compare_chain_(chain, our_current_position, block))
1777                 return false;
1778         delete block;
1779
1780         printf("SV[P]A\tinsert PADDING before\n");
1781         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1782                 return die_("creating PADDING block");
1783         padding->set_length(17);
1784         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1785                 return die_("copying metadata");
1786         if(!iterator.insert_block_before(padding))
1787                 return die_("iterator.insert_block_before(padding)");
1788
1789         block = iterator.get_block();
1790         if(!compare_chain_(chain, our_current_position, block))
1791                 return false;
1792         delete block;
1793
1794         printf("SV[P]PA\tinsert PADDING before\n");
1795         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1796                 return die_("creating PADDING block");
1797         padding->set_length(0);
1798         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1799                 return die_("copying metadata");
1800         if(!iterator.insert_block_before(padding))
1801                 return die_("iterator.insert_block_before(padding)");
1802
1803         block = iterator.get_block();
1804         if(!compare_chain_(chain, our_current_position, block))
1805                 return false;
1806         delete block;
1807
1808         printf("SV[P]PPA\tnext\n");
1809         if(!iterator.next())
1810                 return die_("iterator ended early\n");
1811         our_current_position++;
1812
1813         printf("SVP[P]PA\tnext\n");
1814         if(!iterator.next())
1815                 return die_("iterator ended early\n");
1816         our_current_position++;
1817
1818         printf("SVPP[P]A\tnext\n");
1819         if(!iterator.next())
1820                 return die_("iterator ended early\n");
1821         our_current_position++;
1822
1823         printf("SVPPP[A]\tinsert PADDING after\n");
1824         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1825                 return die_("creating PADDING block");
1826         padding->set_length(57);
1827         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1828                 return die_("copying metadata");
1829         if(!iterator.insert_block_after(padding))
1830                 return die_("iterator.insert_block_after(padding)");
1831
1832         block = iterator.get_block();
1833         if(!compare_chain_(chain, our_current_position, block))
1834                 return false;
1835         delete block;
1836
1837         printf("SVPPPA[P]\tinsert PADDING before\n");
1838         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1839                 return die_("creating PADDING block");
1840         padding->set_length(99);
1841         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1842                 return die_("copying metadata");
1843         if(!iterator.insert_block_before(padding))
1844                 return die_("iterator.insert_block_before(padding)");
1845
1846         block = iterator.get_block();
1847         if(!compare_chain_(chain, our_current_position, block))
1848                 return false;
1849         delete block;
1850
1851         }
1852         our_current_position = 0;
1853
1854         printf("SVPPPAPP\tmerge padding\n");
1855         chain.merge_padding();
1856         add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[3]->get_length());
1857         add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[4]->get_length());
1858         add_to_padding_length_(6, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[7]->get_length());
1859         delete_from_our_metadata_(7);
1860         delete_from_our_metadata_(4);
1861         delete_from_our_metadata_(3);
1862
1863         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1864                 return die_c_("during chain.write(true, false)", chain.status());
1865         if(!compare_chain_(chain, 0, 0))
1866                 return false;
1867         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1868                 return false;
1869
1870         printf("SVPAP\tsort padding\n");
1871         chain.sort_padding();
1872         add_to_padding_length_(4, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[2]->get_length());
1873         delete_from_our_metadata_(2);
1874
1875         if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfile_))
1876                 return die_c_("during chain.write(true, false)", chain.status());
1877         if(!compare_chain_(chain, 0, 0))
1878                 return false;
1879         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1880                 return false;
1881
1882         printf("create iterator\n");
1883         {
1884         FLAC::Metadata::Iterator iterator;
1885         if(!iterator.is_valid())
1886                 return die_("allocating memory for iterator");
1887
1888         our_current_position = 0;
1889
1890         iterator.init(chain);
1891
1892         printf("[S]VAP\tnext\n");
1893         if(!iterator.next())
1894                 return die_("iterator ended early\n");
1895         our_current_position++;
1896
1897         printf("S[V]AP\tnext\n");
1898         if(!iterator.next())
1899                 return die_("iterator ended early\n");
1900         our_current_position++;
1901
1902         printf("SV[A]P\tdelete middle block, replace with padding\n");
1903         if(0 == (padding = new FLAC::Metadata::Padding()))
1904                 return die_("creating PADDING block");
1905         padding->set_length(71);
1906         if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1907                 return die_("copying object");
1908         if(!iterator.delete_block(/*replace_with_padding=*/true))
1909                 return die_c_("iterator.delete_block(true)", chain.status());
1910
1911         block = iterator.get_block();
1912         if(!compare_chain_(chain, our_current_position, block))
1913                 return false;
1914         delete block;
1915
1916         printf("S[V]PP\tnext\n");
1917         if(!iterator.next())
1918                 return die_("iterator ended early\n");
1919         our_current_position++;
1920
1921         printf("SV[P]P\tdelete middle block, don't replace with padding\n");
1922         delete_from_our_metadata_(our_current_position--);
1923         if(!iterator.delete_block(/*replace_with_padding=*/false))
1924                 return die_c_("iterator.delete_block(false)", chain.status());
1925
1926         block = iterator.get_block();
1927         if(!compare_chain_(chain, our_current_position, block))
1928                 return false;
1929         delete block;
1930
1931         printf("S[V]P\tnext\n");
1932         if(!iterator.next())
1933                 return die_("iterator ended early\n");
1934         our_current_position++;
1935
1936         printf("SV[P]\tdelete last block, replace with padding\n");
1937         if(0 == (padding = new FLAC::Metadata::Padding()))
1938                 return die_("creating PADDING block");
1939         padding->set_length(219);
1940         if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1941                 return die_("copying object");
1942         if(!iterator.delete_block(/*replace_with_padding=*/true))
1943                 return die_c_("iterator.delete_block(true)", chain.status());
1944
1945         block = iterator.get_block();
1946         if(!compare_chain_(chain, our_current_position, block))
1947                 return false;
1948         delete block;
1949
1950         printf("S[V]P\tnext\n");
1951         if(!iterator.next())
1952                 return die_("iterator ended early\n");
1953         our_current_position++;
1954
1955         printf("SV[P]\tdelete last block, don't replace with padding\n");
1956         delete_from_our_metadata_(our_current_position--);
1957         if(!iterator.delete_block(/*replace_with_padding=*/false))
1958                 return die_c_("iterator.delete_block(false)", chain.status());
1959
1960         block = iterator.get_block();
1961         if(!compare_chain_(chain, our_current_position, block))
1962                 return false;
1963         delete block;
1964
1965         printf("S[V]\tprev\n");
1966         if(!iterator.prev())
1967                 return die_("iterator ended early\n");
1968         our_current_position--;
1969
1970         printf("[S]V\tdelete STREAMINFO block, should fail\n");
1971         if(iterator.delete_block(/*replace_with_padding=*/false))
1972                 return die_("iterator.delete_block() on STREAMINFO should have failed but didn't");
1973
1974         block = iterator.get_block();
1975         if(!compare_chain_(chain, our_current_position, block))
1976                 return false;
1977         delete block;
1978
1979         } // delete iterator
1980         our_current_position = 0;
1981
1982         printf("SV\tmerge padding\n");
1983         chain.merge_padding();
1984
1985         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1986                 return die_c_("during chain.write(false, false)", chain.status());
1987         if(!compare_chain_(chain, 0, 0))
1988                 return false;
1989         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1990                 return false;
1991
1992         printf("SV\tsort padding\n");
1993         chain.sort_padding();
1994
1995         if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfile_))
1996                 return die_c_("during chain.write(false, false)", chain.status());
1997         if(!compare_chain_(chain, 0, 0))
1998                 return false;
1999         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
2000                 return false;
2001
2002         if(!remove_file_(flacfile_))
2003                 return false;
2004
2005         return true;
2006 }
2007
2008 static bool test_level_2_misc_()
2009 {
2010         ::FLAC__IOCallbacks callbacks;
2011
2012         memset(&callbacks, 0, sizeof(callbacks));
2013         callbacks.read = (::FLAC__IOCallback_Read)fread;
2014 #ifdef FLAC__VALGRIND_TESTING
2015         callbacks.write = chain_write_cb_;
2016 #else
2017         callbacks.write = (::FLAC__IOCallback_Write)fwrite;
2018 #endif
2019         callbacks.seek = chain_seek_cb_;
2020         callbacks.tell = chain_tell_cb_;
2021         callbacks.eof = chain_eof_cb_;
2022
2023         printf("\n\n++++++ testing level 2 interface (mismatched read/write protections)\n");
2024
2025         printf("generate file\n");
2026
2027         if(!generate_file_(/*include_extras=*/false))
2028                 return false;
2029
2030         printf("create chain\n");
2031         FLAC::Metadata::Chain chain;
2032         if(!chain.is_valid())
2033                 return die_("allocating chain");
2034
2035         printf("read chain (filename-based)\n");
2036
2037         if(!chain.read(flacfile_))
2038                 return die_c_("reading chain", chain.status());
2039
2040         printf("write chain with wrong method Chain::write(with callbacks)\n");
2041         {
2042                 if(chain.write(/*use_padding=*/false, 0, callbacks))
2043                         return die_c_("mismatched write should have failed", chain.status());
2044                 if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2045                         return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2046                 printf("  OK: Chain::write(with callbacks) returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2047         }
2048
2049         printf("read chain (filename-based)\n");
2050
2051         if(!chain.read(flacfile_))
2052                 return die_c_("reading chain", chain.status());
2053
2054         printf("write chain with wrong method Chain::write(with callbacks and tempfile)\n");
2055         {
2056                 if(chain.write(/*use_padding=*/false, 0, callbacks, 0, callbacks))
2057                         return die_c_("mismatched write should have failed", chain.status());
2058                 if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2059                         return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2060                 printf("  OK: Chain::write(with callbacks and tempfile) returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2061         }
2062
2063         printf("read chain (callback-based)\n");
2064         {
2065                 FILE *file = fopen(flacfile_, "rb");
2066                 if(0 == file)
2067                         return die_("opening file");
2068                 if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2069                         fclose(file);
2070                         return die_c_("reading chain", chain.status());
2071                 }
2072                 fclose(file);
2073         }
2074
2075         printf("write chain with wrong method write()\n");
2076         {
2077                 if(chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
2078                         return die_c_("mismatched write should have failed", chain.status());
2079                 if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH)
2080                         return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", chain.status());
2081                 printf("  OK: write() returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n");
2082         }
2083
2084         printf("read chain (callback-based)\n");
2085         {
2086                 FILE *file = fopen(flacfile_, "rb");
2087                 if(0 == file)
2088                         return die_("opening file");
2089                 if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2090                         fclose(file);
2091                         return die_c_("reading chain", chain.status());
2092                 }
2093                 fclose(file);
2094         }
2095
2096         printf("testing Chain::check_if_tempfile_needed()... ");
2097
2098         if(!chain.check_if_tempfile_needed(/*use_padding=*/false))
2099                 printf("OK: Chain::check_if_tempfile_needed() returned false like it should\n");
2100         else
2101                 return die_("Chain::check_if_tempfile_needed() returned true but shouldn't have");
2102
2103         printf("write chain with wrong method Chain::write(with callbacks and tempfile)\n");
2104         {
2105                 if(chain.write(/*use_padding=*/false, 0, callbacks, 0, callbacks))
2106                         return die_c_("mismatched write should have failed", chain.status());
2107                 if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL)
2108                         return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", chain.status());
2109                 printf("  OK: Chain::write(with callbacks and tempfile) returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n");
2110         }
2111
2112         printf("read chain (callback-based)\n");
2113         {
2114                 FILE *file = fopen(flacfile_, "rb");
2115                 if(0 == file)
2116                         return die_("opening file");
2117                 if(!chain.read((::FLAC__IOHandle)file, callbacks)) {
2118                         fclose(file);
2119                         return die_c_("reading chain", chain.status());
2120                 }
2121                 fclose(file);
2122         }
2123
2124         printf("create iterator\n");
2125         {
2126         FLAC::Metadata::Iterator iterator;
2127         if(!iterator.is_valid())
2128                 return die_("allocating memory for iterator");
2129
2130         iterator.init(chain);
2131
2132         printf("[S]VP\tnext\n");
2133         if(!iterator.next())
2134                 return die_("iterator ended early\n");
2135
2136         printf("S[V]P\tdelete VORBIS_COMMENT, write\n");
2137         if(!iterator.delete_block(/*replace_with_padding=*/false))
2138                 return die_c_("block delete failed\n", chain.status());
2139
2140         printf("testing Chain::check_if_tempfile_needed()... ");
2141
2142         if(chain.check_if_tempfile_needed(/*use_padding=*/false))
2143                 printf("OK: Chain::check_if_tempfile_needed() returned true like it should\n");
2144         else
2145                 return die_("Chain::check_if_tempfile_needed() returned false but shouldn't have");
2146
2147         printf("write chain with wrong method Chain::write(with callbacks)\n");
2148         {
2149                 if(chain.write(/*use_padding=*/false, 0, callbacks))
2150                         return die_c_("mismatched write should have failed", chain.status());
2151                 if(chain.status() != ::FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL)
2152                         return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", chain.status());
2153                 printf("  OK: Chain::write(with callbacks) returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n");
2154         }
2155
2156         } // delete iterator
2157
2158         if(!remove_file_(flacfile_))
2159                 return false;
2160
2161         return true;
2162 }
2163
2164 bool test_metadata_file_manipulation()
2165 {
2166         printf("\n+++ libFLAC++ unit test: metadata manipulation\n\n");
2167
2168         our_metadata_.num_blocks = 0;
2169
2170         if(!test_level_0_())
2171                 return false;
2172
2173         if(!test_level_1_())
2174                 return false;
2175
2176         if(!test_level_2_(/*filename_based=*/true)) /* filename-based */
2177                 return false;
2178         if(!test_level_2_(/*filename_based=*/false)) /* callback-based */
2179                 return false;
2180         if(!test_level_2_misc_())
2181                 return false;
2182
2183         return true;
2184 }