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