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