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