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