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