work related to moving some file utils into the new file_utils convenience lib
[flac.git] / src / test_libFLAC++ / metadata_manip.cpp
1 /* test_libFLAC++ - Unit tester for libFLAC++
2  * Copyright (C) 2002  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/file_utils.h"
26 #include <stdio.h>
27 #include <stdlib.h> /* for malloc() */
28 #include <string.h> /* for memcpy()/memset() */
29
30 /******************************************************************************
31         The general strategy of these tests (for interface levels 1 and 2) is
32         to create a dummy FLAC file with a known set of initial metadata
33         blocks, then keep a mirror locally of what we expect the metadata to be
34         after each operation.  Then testing becomes a simple matter of running
35         a FLAC::Decoder::File over the dummy file after each operation, comparing
36         the decoded metadata to what's in our local copy.  If there are any
37         differences in the metadata, or the actual audio data is corrupted, we
38         will catch it while decoding.
39 ******************************************************************************/
40
41 class OurFileDecoder: public FLAC::Decoder::File {
42 public:
43         inline OurFileDecoder(bool ignore_metadata): ignore_metadata_(ignore_metadata), error_occurred_(false) { }
44
45         bool ignore_metadata_;;
46         bool error_occurred_;
47 protected:
48         ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
49         void metadata_callback(const ::FLAC__StreamMetadata *metadata);
50         void error_callback(::FLAC__StreamDecoderErrorStatus status);
51 };
52
53 struct OurMetadata {
54         FLAC::Metadata::Prototype *blocks[64];
55         unsigned num_blocks;
56 };
57
58 static const char *flacfile_ = "metadata.flac";
59
60 /* our copy of the metadata in flacfile_ */
61 static OurMetadata our_metadata_;
62
63 /* the current block number that corresponds to the position of the iterator we are testing */
64 static unsigned mc_our_block_number_ = 0;
65
66 static bool die_(const char *msg)
67 {
68         printf("ERROR: %s\n", msg);
69         return false;
70 }
71
72 static bool die_c_(const char *msg, FLAC::Metadata::Chain::Status status)
73 {
74         printf("ERROR: %s\n", msg);
75         printf("       status=%u (%s)\n", (unsigned)((::FLAC__Metadata_ChainStatus)status), status.as_cstring());
76         return false;
77 }
78
79 static bool die_ss_(const char *msg, FLAC::Metadata::SimpleIterator &iterator)
80 {
81         const FLAC::Metadata::SimpleIterator::Status status = iterator.status();
82         printf("ERROR: %s\n", msg);
83         printf("       status=%u (%s)\n", (unsigned)((::FLAC__Metadata_SimpleIteratorStatus)status), status.as_cstring());
84         return false;
85 }
86
87 static void *malloc_or_die_(size_t size)
88 {
89         void *x = malloc(size);
90         if(0 == x) {
91                 fprintf(stderr, "ERROR: out of memory allocating %u bytes\n", (unsigned)size);
92                 exit(1);
93         }
94         return x;
95 }
96
97 /* functions for working with our metadata copy */
98
99 static bool replace_in_our_metadata_(FLAC::Metadata::Prototype *block, unsigned position, bool copy)
100 {
101         unsigned i;
102         FLAC::Metadata::Prototype *obj = block;
103         FLAC__ASSERT(position < our_metadata_.num_blocks);
104         if(copy) {
105                 if(0 == (obj = FLAC::Metadata::clone(block)))
106                         return die_("during FLAC::Metadata::clone()");
107         }
108         delete our_metadata_.blocks[position];
109         our_metadata_.blocks[position] = obj;
110
111         /* set the is_last flags */
112         for(i = 0; i < our_metadata_.num_blocks - 1; i++)
113                 our_metadata_.blocks[i]->set_is_last(false);
114         our_metadata_.blocks[i]->set_is_last(true);
115
116         return true;
117 }
118
119 static bool insert_to_our_metadata_(FLAC::Metadata::Prototype *block, unsigned position, bool copy)
120 {
121         unsigned i;
122         FLAC::Metadata::Prototype *obj = block;
123         if(copy) {
124                 if(0 == (obj = FLAC::Metadata::clone(block)))
125                         return die_("during FLAC::Metadata::clone()");
126         }
127         if(position > our_metadata_.num_blocks) {
128                 position = our_metadata_.num_blocks;
129         }
130         else {
131                 for(i = our_metadata_.num_blocks; i > position; i--)
132                         our_metadata_.blocks[i] = our_metadata_.blocks[i-1];
133         }
134         our_metadata_.blocks[position] = obj;
135         our_metadata_.num_blocks++;
136
137         /* set the is_last flags */
138         for(i = 0; i < our_metadata_.num_blocks - 1; i++)
139                 our_metadata_.blocks[i]->set_is_last(false);
140         our_metadata_.blocks[i]->set_is_last(true);
141
142         return true;
143 }
144
145 static void delete_from_our_metadata_(unsigned position)
146 {
147         unsigned i;
148         FLAC__ASSERT(position < our_metadata_.num_blocks);
149         delete our_metadata_.blocks[position];
150         for(i = position; i < our_metadata_.num_blocks - 1; i++)
151                 our_metadata_.blocks[i] = our_metadata_.blocks[i+1];
152         our_metadata_.num_blocks--;
153
154         /* set the is_last flags */
155         if(our_metadata_.num_blocks > 0) {
156                 for(i = 0; i < our_metadata_.num_blocks - 1; i++)
157                         our_metadata_.blocks[i]->set_is_last(false);
158                 our_metadata_.blocks[i]->set_is_last(true);
159         }
160 }
161
162 void add_to_padding_length_(unsigned index, int delta)
163 {
164         FLAC::Metadata::Padding *padding = dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[index]);
165         FLAC__ASSERT(0 != padding);
166         padding->set_length((unsigned)((int)padding->get_length() + delta));
167 }
168
169 /* function for comparing our metadata to a FLAC::Metadata::Chain */
170
171 static bool compare_chain_(FLAC::Metadata::Chain &chain, unsigned current_position, FLAC::Metadata::Prototype *current_block)
172 {
173         unsigned i;
174         FLAC::Metadata::Iterator iterator;
175         FLAC::Metadata::Prototype *block;
176         bool next_ok = true;
177
178         printf("\tcomparing chain... ");
179         fflush(stdout);
180
181         if(!iterator.is_valid())
182                 return die_("allocating memory for iterator");
183
184         iterator.init(chain);
185
186         i = 0;
187         do {
188                 printf("%u... ", i);
189                 fflush(stdout);
190
191                 if(0 == (block = iterator.get_block()))
192                         return die_("getting block from iterator");
193
194                 if(*block != *our_metadata_.blocks[i])
195                         return die_("metadata block mismatch");
196
197                 i++;
198                 next_ok = iterator.next();
199         } while(i < our_metadata_.num_blocks && next_ok);
200
201         if(next_ok)
202                 return die_("chain has more blocks than expected");
203
204         if(i < our_metadata_.num_blocks)
205                 return die_("short block count in chain");
206
207         if(0 != current_block) {
208                 printf("CURRENT_POSITION... ");
209                 fflush(stdout);
210
211                 if(*current_block != *our_metadata_.blocks[current_position])
212                         return die_("metadata block mismatch");
213         }
214
215         printf("PASSED\n");
216
217         return true;
218 }
219
220 ::FLAC__StreamDecoderWriteStatus OurFileDecoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[])
221 {
222         (void)buffer;
223
224         if(
225                 (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER && frame->header.number.frame_number == 0) ||
226                 (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER && frame->header.number.sample_number == 0)
227         ) {
228                 printf("content... ");
229                 fflush(stdout);
230         }
231
232         return ::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
233 }
234
235 void OurFileDecoder::metadata_callback(const ::FLAC__StreamMetadata *metadata)
236 {
237         /* don't bother checking if we've already hit an error */
238         if(error_occurred_)
239                 return;
240
241         printf("%d... ", mc_our_block_number_);
242         fflush(stdout);
243
244         if(!ignore_metadata_) {
245                 if(mc_our_block_number_ >= our_metadata_.num_blocks) {
246                         (void)die_("got more metadata blocks than expected");
247                         error_occurred_ = true;
248                 }
249                 else {
250                         if(*our_metadata_.blocks[mc_our_block_number_] != metadata) {
251                                 (void)die_("metadata block mismatch");
252                                 error_occurred_ = true;
253                         }
254                 }
255         }
256
257         mc_our_block_number_++;
258 }
259
260 void OurFileDecoder::error_callback(::FLAC__StreamDecoderErrorStatus status)
261 {
262         error_occurred_ = true;
263         printf("ERROR: got error callback, status = %s (%u)\n", FLAC__StreamDecoderErrorStatusString[status], (unsigned)status);
264 }
265
266 static bool generate_file_()
267 {
268         ::FLAC__StreamMetadata streaminfo, vorbiscomment, padding;
269         ::FLAC__StreamMetadata *metadata[1];
270
271         printf("generating FLAC file for test\n");
272
273         while(our_metadata_.num_blocks > 0)
274                 delete_from_our_metadata_(0);
275
276         streaminfo.is_last = false;
277         streaminfo.type = ::FLAC__METADATA_TYPE_STREAMINFO;
278         streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
279         streaminfo.data.stream_info.min_blocksize = 576;
280         streaminfo.data.stream_info.max_blocksize = 576;
281         streaminfo.data.stream_info.min_framesize = 0;
282         streaminfo.data.stream_info.max_framesize = 0;
283         streaminfo.data.stream_info.sample_rate = 44100;
284         streaminfo.data.stream_info.channels = 1;
285         streaminfo.data.stream_info.bits_per_sample = 8;
286         streaminfo.data.stream_info.total_samples = 0;
287         memset(streaminfo.data.stream_info.md5sum, 0, 16);
288
289         {
290                 const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING);
291                 vorbiscomment.is_last = false;
292                 vorbiscomment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT;
293                 vorbiscomment.length = (4 + vendor_string_length) + 4;
294                 vorbiscomment.data.vorbis_comment.vendor_string.length = vendor_string_length;
295                 vorbiscomment.data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length);
296                 memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length);
297                 vorbiscomment.data.vorbis_comment.num_comments = 0;
298                 vorbiscomment.data.vorbis_comment.comments = 0;
299         }
300
301         padding.is_last = true;
302         padding.type = ::FLAC__METADATA_TYPE_PADDING;
303         padding.length = 1234;
304
305         metadata[0] = &padding;
306
307         FLAC::Metadata::StreamInfo s(&streaminfo);
308         FLAC::Metadata::VorbisComment v(&vorbiscomment);
309         FLAC::Metadata::Padding p(&padding);
310         if(
311                 !insert_to_our_metadata_(&s, 0, /*copy=*/true) ||
312                 !insert_to_our_metadata_(&v, 1, /*copy=*/true) ||
313                 !insert_to_our_metadata_(&p, 2, /*copy=*/true)
314         )
315                 return die_("priming our metadata");
316
317         if(!file_utils__generate_flacfile(flacfile_, 0, 512 * 1024, &streaminfo, metadata, 1))
318                 return die_("creating the encoded file");
319
320         free(vorbiscomment.data.vorbis_comment.vendor_string.entry);
321
322         return true;
323 }
324
325 static bool test_file_(const char *filename, bool ignore_metadata)
326 {
327         OurFileDecoder decoder(ignore_metadata);
328
329         FLAC__ASSERT(0 != filename);
330
331         mc_our_block_number_ = 0;
332         decoder.error_occurred_ = false;
333
334         printf("\ttesting '%s'... ", filename);
335         fflush(stdout);
336
337         if(!decoder.is_valid())
338                 return die_("couldn't allocate decoder instance");
339
340         decoder.set_md5_checking(true);
341         decoder.set_filename(filename);
342         decoder.set_metadata_respond_all();
343         if(decoder.init() != ::FLAC__FILE_DECODER_OK) {
344                 decoder.finish();
345                 return die_("initializing decoder\n");
346         }
347         if(!decoder.process_until_end_of_file()) {
348                 decoder.finish();
349                 return die_("decoding file\n");
350         }
351
352         decoder.finish();
353
354         if(decoder.error_occurred_)
355                 return false;
356
357         if(mc_our_block_number_ != our_metadata_.num_blocks)
358                 return die_("short metadata block count");
359
360         printf("PASSED\n");
361         return true;
362 }
363
364 static bool change_stats_(const char *filename, bool read_only)
365 {
366         if(!FLAC__file_utils_change_stats(filename, read_only))
367                 return die_("during FLAC__file_utils_change_stats()");
368
369         return true;
370 }
371
372 static bool remove_file_(const char *filename)
373 {
374         while(our_metadata_.num_blocks > 0)
375                 delete_from_our_metadata_(0);
376
377         if(!FLAC__file_utils_remove_file(filename))
378                 return die_("removing file");
379
380         return true;
381 }
382
383 static bool test_level_0_()
384 {
385         FLAC::Metadata::StreamInfo streaminfo;
386
387         printf("\n\n++++++ testing level 0 interface\n");
388
389         if(!generate_file_())
390                 return false;
391
392         if(!test_file_(flacfile_, /*ignore_metadata=*/true))
393                 return false;
394
395         if(!FLAC::Metadata::get_streaminfo(flacfile_, streaminfo))
396                 return die_("during FLAC::Metadata::get_streaminfo()");
397
398         /* check to see if some basic data matches (c.f. generate_file_()) */
399         if(streaminfo.get_channels() != 1)
400                 return die_("mismatch in streaminfo.get_channels()");
401         if(streaminfo.get_bits_per_sample() != 8)
402                 return die_("mismatch in streaminfo.get_bits_per_sample()");
403         if(streaminfo.get_sample_rate() != 44100)
404                 return die_("mismatch in streaminfo.get_sample_rate()");
405         if(streaminfo.get_min_blocksize() != 576)
406                 return die_("mismatch in streaminfo.get_min_blocksize()");
407         if(streaminfo.get_max_blocksize() != 576)
408                 return die_("mismatch in streaminfo.get_max_blocksize()");
409
410         if(!remove_file_(flacfile_))
411                 return false;
412
413         return true;
414 }
415
416 static bool test_level_1_()
417 {
418         FLAC::Metadata::Prototype *block;
419         FLAC::Metadata::StreamInfo *streaminfo;
420         FLAC::Metadata::Padding *padding;
421         FLAC::Metadata::Application *app;
422         FLAC__byte data[1000];
423         unsigned our_current_position = 0;
424
425         printf("\n\n++++++ testing level 1 interface\n");
426
427         /************************************************************/
428         {
429         printf("simple iterator on read-only file\n");
430
431         if(!generate_file_())
432                 return false;
433
434         if(!change_stats_(flacfile_, /*read_only=*/true))
435                 return false;
436
437         if(!test_file_(flacfile_, /*ignore_metadata=*/true))
438                 return false;
439
440         FLAC::Metadata::SimpleIterator iterator;
441
442         if(!iterator.is_valid())
443                 return die_("iterator.is_valid() returned false");
444
445         if(!iterator.init(flacfile_, /*read_only=*/false, /*preserve_file_stats=*/false))
446                 return die_("iterator.init() returned false");
447
448         printf("is writable = %u\n", (unsigned)iterator.is_writable());
449         if(iterator.is_writable())
450                 return die_("iterator claims file is writable when tester thinks it should not be; are you running as root?\n");
451
452         printf("iterate forwards\n");
453
454         if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
455                 return die_("expected STREAMINFO type from iterator.get_block_type()");
456         if(0 == (block = iterator.get_block()))
457                 return die_("getting block 0");
458         if(block->get_type() != ::FLAC__METADATA_TYPE_STREAMINFO)
459                 return die_("expected STREAMINFO type");
460         if(block->get_is_last())
461                 return die_("expected is_last to be false");
462         if(block->get_length() != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
463                 return die_("bad STREAMINFO length");
464         /* check to see if some basic data matches (c.f. generate_file_()) */
465         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
466         FLAC__ASSERT(0 != streaminfo);
467         if(streaminfo->get_channels() != 1)
468                 return die_("mismatch in channels");
469         if(streaminfo->get_bits_per_sample() != 8)
470                 return die_("mismatch in bits_per_sample");
471         if(streaminfo->get_sample_rate() != 44100)
472                 return die_("mismatch in sample_rate");
473         if(streaminfo->get_min_blocksize() != 576)
474                 return die_("mismatch in min_blocksize");
475         if(streaminfo->get_max_blocksize() != 576)
476                 return die_("mismatch in max_blocksize");
477
478         if(!iterator.next())
479                 return die_("forward iterator ended early");
480         our_current_position++;
481
482         if(!iterator.next())
483                 return die_("forward iterator ended early");
484         our_current_position++;
485
486         if(iterator.get_block_type() != ::FLAC__METADATA_TYPE_PADDING)
487                 return die_("expected PADDING type from iterator.get_block_type()");
488         if(0 == (block = iterator.get_block()))
489                 return die_("getting block 1");
490         if(block->get_type() != ::FLAC__METADATA_TYPE_PADDING)
491                 return die_("expected PADDING type");
492         if(!block->get_is_last())
493                 return die_("expected is_last to be true");
494         /* check to see if some basic data matches (c.f. generate_file_()) */
495         if(block->get_length() != 1234)
496                 return die_("bad PADDING length");
497
498         if(iterator.next())
499                 return die_("forward iterator returned true but should have returned false");
500
501         printf("iterate backwards\n");
502         if(!iterator.prev())
503                 return die_("reverse iterator ended early");
504         if(!iterator.prev())
505                 return die_("reverse iterator ended early");
506         if(iterator.prev())
507                 return die_("reverse iterator returned true but should have returned false");
508
509         printf("testing iterator.set_block() on read-only file...\n");
510
511         if(!iterator.set_block(streaminfo, false))
512                 printf("PASSED.  iterator.set_block() returned false like it should\n");
513         else
514                 return die_("iterator.set_block() returned true but shouldn't have");
515         }
516
517         /************************************************************/
518         {
519         printf("simple iterator on writable file\n");
520
521         if(!change_stats_(flacfile_, /*read-only=*/false))
522                 return false;
523
524         printf("creating APPLICATION block\n");
525
526         if(0 == (app = new FLAC::Metadata::Application()))
527                 return die_("new FLAC::Metadata::Application()");
528         app->set_id((const unsigned char *)"duh");
529
530         printf("creating PADDING block\n");
531
532         if(0 == (padding = new FLAC::Metadata::Padding()))
533                 return die_("new FLAC::Metadata::Padding()");
534         padding->set_length(20);
535
536         FLAC::Metadata::SimpleIterator iterator;
537
538         if(!iterator.is_valid())
539                 return die_("iterator.is_valid() returned false");
540
541         if(!iterator.init(flacfile_, /*read_only=*/false, /*preserve_file_stats=*/false))
542                 return die_("iterator.init() returned false");
543         our_current_position = 0;
544
545         printf("is writable = %u\n", (unsigned)iterator.is_writable());
546
547         printf("[S]VP\ttry to write over STREAMINFO block...\n");
548         if(!iterator.set_block(app, false))
549                 printf("\titerator.set_block() returned false like it should\n");
550         else
551                 return die_("iterator.set_block() returned true but shouldn't have");
552
553         printf("[S]VP\tnext\n");
554         if(!iterator.next())
555                 return die_("iterator ended early\n");
556         our_current_position++;
557
558         printf("S[V]P\tnext\n");
559         if(!iterator.next())
560                 return die_("iterator ended early\n");
561         our_current_position++;
562
563         printf("SV[P]\tinsert PADDING after, don't expand into padding\n");
564         padding->set_length(25);
565         if(!iterator.insert_block_after(padding, false))
566                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
567         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
568                 return false;
569
570         printf("SVP[P]\tprev\n");
571         if(!iterator.prev())
572                 return die_("iterator ended early\n");
573         our_current_position--;
574
575         printf("SV[P]P\tprev\n");
576         if(!iterator.prev())
577                 return die_("iterator ended early\n");
578         our_current_position--;
579
580         printf("S[V]PP\tinsert PADDING after, don't expand into padding\n");
581         padding->set_length(30);
582         if(!iterator.insert_block_after(padding, false))
583                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
584         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
585                 return false;
586
587         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
588                 return false;
589
590         printf("SV[P]PP\tprev\n");
591         if(!iterator.prev())
592                 return die_("iterator ended early\n");
593         our_current_position--;
594
595         printf("S[V]PPP\tprev\n");
596         if(!iterator.prev())
597                 return die_("iterator ended early\n");
598         our_current_position--;
599
600         printf("[S]VPPP\tdelete (STREAMINFO block), must fail\n");
601         if(iterator.delete_block(false))
602                 return die_ss_("iterator.delete_block(false) should have returned false", iterator);
603
604         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
605                 return false;
606
607         printf("[S]VPPP\tnext\n");
608         if(!iterator.next())
609                 return die_("iterator ended early\n");
610         our_current_position++;
611
612         printf("S[V]PPP\tnext\n");
613         if(!iterator.next())
614                 return die_("iterator ended early\n");
615         our_current_position++;
616
617         printf("SV[P]PP\tdelete (middle block), replace with padding\n");
618         if(!iterator.delete_block(true))
619                 return die_ss_("iterator.delete_block(true)", iterator);
620         our_current_position--;
621
622         printf("S[V]PPP\tnext\n");
623         if(!iterator.next())
624                 return die_("iterator ended early\n");
625         our_current_position++;
626
627         printf("SV[P]PP\tdelete (middle block), don't replace with padding\n");
628         if(!iterator.delete_block(false))
629                 return die_ss_("iterator.delete_block(false)", iterator);
630         delete_from_our_metadata_(our_current_position--);
631
632         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
633                 return false;
634
635         printf("S[V]PP\tnext\n");
636         if(!iterator.next())
637                 return die_("iterator ended early\n");
638         our_current_position++;
639
640         printf("SV[P]P\tnext\n");
641         if(!iterator.next())
642                 return die_("iterator ended early\n");
643         our_current_position++;
644
645         printf("SVP[P]\tdelete (last block), replace with padding\n");
646         if(!iterator.delete_block(true))
647                 return die_ss_("iterator.delete_block(false)", iterator);
648         our_current_position--;
649
650         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
651                 return false;
652
653         printf("SV[P]P\tnext\n");
654         if(!iterator.next())
655                 return die_("iterator ended early\n");
656         our_current_position++;
657
658         printf("SVP[P]\tdelete (last block), don't replace with padding\n");
659         if(!iterator.delete_block(false))
660                 return die_ss_("iterator.delete_block(false)", iterator);
661         delete_from_our_metadata_(our_current_position--);
662
663         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
664                 return false;
665
666         printf("SV[P]\tprev\n");
667         if(!iterator.prev())
668                 return die_("iterator ended early\n");
669         our_current_position--;
670
671         printf("S[V]P\tprev\n");
672         if(!iterator.prev())
673                 return die_("iterator ended early\n");
674         our_current_position--;
675
676         printf("[S]VP\tset STREAMINFO (change sample rate)\n");
677         FLAC__ASSERT(our_current_position == 0);
678         block = iterator.get_block();
679         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
680         FLAC__ASSERT(0 != streaminfo);
681         streaminfo->set_sample_rate(32000);
682         if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
683                 return die_("copying object");
684         if(!iterator.set_block(block, false))
685                 return die_ss_("iterator.set_block(block, false)", iterator);
686         delete block;
687
688         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
689                 return false;
690
691         printf("[S]VP\tnext\n");
692         if(!iterator.next())
693                 return die_("iterator ended early\n");
694         our_current_position++;
695
696         printf("S[V]P\tinsert APPLICATION after, expand into padding of exceeding size\n");
697         app->set_id((const unsigned char *)"euh"); /* twiddle the id so that our comparison doesn't miss transposition */
698         if(!iterator.insert_block_after(app, true))
699                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
700         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
701                 return false;
702         add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
703
704         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
705                 return false;
706
707         printf("SV[A]P\tnext\n");
708         if(!iterator.next())
709                 return die_("iterator ended early\n");
710         our_current_position++;
711
712         printf("SVA[P]\tset APPLICATION, expand into padding of exceeding size\n");
713         app->set_id((const unsigned char *)"fuh"); /* twiddle the id */
714         if(!iterator.set_block(app, true))
715                 return die_ss_("iterator.set_block(app, true)", iterator);
716         if(!insert_to_our_metadata_(app, our_current_position, /*copy=*/true))
717                 return false;
718         add_to_padding_length_(our_current_position+1, -((int)(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + (int)app->get_length()));
719
720         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
721                 return false;
722
723         printf("SVA[A]P\tset APPLICATION (grow), don't expand into padding\n");
724         app->set_id((const unsigned char *)"guh"); /* twiddle the id */
725         if(!app->set_data(data, sizeof(data), true))
726                 return die_("setting APPLICATION data");
727         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
728                 return die_("copying object");
729         if(!iterator.set_block(app, false))
730                 return die_ss_("iterator.set_block(app, false)", iterator);
731
732         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
733                 return false;
734
735         printf("SVA[A]P\tset APPLICATION (shrink), don't fill in with padding\n");
736         app->set_id((const unsigned char *)"huh"); /* twiddle the id */
737         if(!app->set_data(data, 12, true))
738                 return die_("setting APPLICATION data");
739         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
740                 return die_("copying object");
741         if(!iterator.set_block(app, false))
742                 return die_ss_("iterator.set_block(app, false)", iterator);
743
744         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
745                 return false;
746
747         printf("SVA[A]P\tset APPLICATION (grow), expand into padding of exceeding size\n");
748         app->set_id((const unsigned char *)"iuh"); /* twiddle the id */
749         if(!app->set_data(data, sizeof(data), true))
750                 return die_("setting APPLICATION data");
751         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
752                 return die_("copying object");
753         add_to_padding_length_(our_current_position+1, -((int)sizeof(data) - 12));
754         if(!iterator.set_block(app, true))
755                 return die_ss_("iterator.set_block(app, true)", iterator);
756
757         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
758                 return false;
759
760         printf("SVA[A]P\tset APPLICATION (shrink), fill in with padding\n");
761         app->set_id((const unsigned char *)"juh"); /* twiddle the id */
762         if(!app->set_data(data, 23, true))
763                 return die_("setting APPLICATION data");
764         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
765                 return die_("copying object");
766         if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/true))
767                 return die_("copying object");
768         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(sizeof(data) - 23 - FLAC__STREAM_METADATA_HEADER_LENGTH);
769         if(!iterator.set_block(app, true))
770                 return die_ss_("iterator.set_block(app, true)", iterator);
771
772         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
773                 return false;
774
775         printf("SVA[A]PP\tnext\n");
776         if(!iterator.next())
777                 return die_("iterator ended early\n");
778         our_current_position++;
779
780         printf("SVAA[P]P\tnext\n");
781         if(!iterator.next())
782                 return die_("iterator ended early\n");
783         our_current_position++;
784
785         printf("SVAAP[P]\tset PADDING (shrink), don't fill in with padding\n");
786         padding->set_length(5);
787         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
788                 return die_("copying object");
789         if(!iterator.set_block(padding, false))
790                 return die_ss_("iterator.set_block(padding, false)", iterator);
791
792         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
793                 return false;
794
795         printf("SVAAP[P]\tset APPLICATION (grow)\n");
796         app->set_id((const unsigned char *)"kuh"); /* twiddle the id */
797         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
798                 return die_("copying object");
799         if(!iterator.set_block(app, false))
800                 return die_ss_("iterator.set_block(app, false)", iterator);
801
802         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
803                 return false;
804
805         printf("SVAAP[A]\tset PADDING (equal)\n");
806         padding->set_length(27);
807         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
808                 return die_("copying object");
809         if(!iterator.set_block(padding, false))
810                 return die_ss_("iterator.set_block(padding, false)", iterator);
811
812         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
813                 return false;
814
815         printf("SVAAP[P]\tprev\n");
816         if(!iterator.prev())
817                 return die_("iterator ended early\n");
818         our_current_position--;
819
820         printf("SVAA[P]P\tdelete (middle block), don't replace with padding\n");
821         if(!iterator.delete_block(false))
822                 return die_ss_("iterator.delete_block(false)", iterator);
823         delete_from_our_metadata_(our_current_position--);
824
825         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
826                 return false;
827
828         printf("SVA[A]P\tdelete (middle block), don't replace with padding\n");
829         if(!iterator.delete_block(false))
830                 return die_ss_("iterator.delete_block(false)", iterator);
831         delete_from_our_metadata_(our_current_position--);
832
833         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
834                 return false;
835
836         printf("SV[A]P\tnext\n");
837         if(!iterator.next())
838                 return die_("iterator ended early\n");
839         our_current_position++;
840
841         printf("SVA[P]\tinsert PADDING after\n");
842         padding->set_length(5);
843         if(!iterator.insert_block_after(padding, false))
844                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
845         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
846                 return false;
847
848         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
849                 return false;
850
851         printf("SVAP[P]\tprev\n");
852         if(!iterator.prev())
853                 return die_("iterator ended early\n");
854         our_current_position--;
855
856         printf("SVA[P]P\tprev\n");
857         if(!iterator.prev())
858                 return die_("iterator ended early\n");
859         our_current_position--;
860
861         printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is too small\n");
862         if(!app->set_data(data, 32, true))
863                 return die_("setting APPLICATION data");
864         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
865                 return die_("copying object");
866         if(!iterator.set_block(app, true))
867                 return die_ss_("iterator.set_block(app, true)", iterator);
868
869         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
870                 return false;
871
872         printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is 'close' but still too small\n");
873         if(!app->set_data(data, 60, true))
874                 return die_("setting APPLICATION data");
875         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
876                 return die_("copying object");
877         if(!iterator.set_block(app, true))
878                 return die_ss_("iterator.set_block(app, true)", iterator);
879
880         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
881                 return false;
882
883         printf("SV[A]PP\tset APPLICATION (grow), expand into padding which will leave 0-length pad\n");
884         if(!app->set_data(data, 87, true))
885                 return die_("setting APPLICATION data");
886         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
887                 return die_("copying object");
888         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
889         if(!iterator.set_block(app, true))
890                 return die_ss_("iterator.set_block(app, true)", iterator);
891
892         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
893                 return false;
894
895         printf("SV[A]PP\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
896         if(!app->set_data(data, 91, true))
897                 return die_("setting APPLICATION data");
898         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
899                 return die_("copying object");
900         delete_from_our_metadata_(our_current_position+1);
901         if(!iterator.set_block(app, true))
902                 return die_ss_("iterator.set_block(app, true)", iterator);
903
904         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
905                 return false;
906
907         printf("SV[A]P\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
908         if(!app->set_data(data, 100, true))
909                 return die_("setting APPLICATION data");
910         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
911                 return die_("copying object");
912         delete_from_our_metadata_(our_current_position+1);
913         our_metadata_.blocks[our_current_position]->set_is_last(true);
914         if(!iterator.set_block(app, true))
915                 return die_ss_("iterator.set_block(app, true)", iterator);
916
917         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
918                 return false;
919
920         printf("SV[A]\tset PADDING (equal size)\n");
921         padding->set_length(app->get_length());
922         if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true))
923                 return die_("copying object");
924         if(!iterator.set_block(padding, true))
925                 return die_ss_("iterator.set_block(padding, true)", iterator);
926
927         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
928                 return false;
929
930         printf("SV[P]\tinsert PADDING after\n");
931         if(!iterator.insert_block_after(padding, false))
932                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
933         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
934                 return false;
935
936         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
937                 return false;
938
939         printf("SVP[P]\tinsert PADDING after\n");
940         padding->set_length(5);
941         if(!iterator.insert_block_after(padding, false))
942                 return die_ss_("iterator.insert_block_after(padding, false)", iterator);
943         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
944                 return false;
945
946         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
947                 return false;
948
949         printf("SVPP[P]\tprev\n");
950         if(!iterator.prev())
951                 return die_("iterator ended early\n");
952         our_current_position--;
953
954         printf("SVP[P]P\tprev\n");
955         if(!iterator.prev())
956                 return die_("iterator ended early\n");
957         our_current_position--;
958
959         printf("SV[P]PP\tprev\n");
960         if(!iterator.prev())
961                 return die_("iterator ended early\n");
962         our_current_position--;
963
964         printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is too small\n");
965         if(!app->set_data(data, 101, true))
966                 return die_("setting APPLICATION data");
967         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
968                 return die_("copying object");
969         if(!iterator.insert_block_after(app, true))
970                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
971
972         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
973                 return false;
974
975         printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
976         if(!iterator.delete_block(false))
977                 return die_ss_("iterator.delete_block(false)", iterator);
978         delete_from_our_metadata_(our_current_position--);
979
980         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
981                 return false;
982
983         printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is 'close' but still too small\n");
984         if(!app->set_data(data, 97, true))
985                 return die_("setting APPLICATION data");
986         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
987                 return die_("copying object");
988         if(!iterator.insert_block_after(app, true))
989                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
990
991         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
992                 return false;
993
994         printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n");
995         if(!iterator.delete_block(false))
996                 return die_ss_("iterator.delete_block(false)", iterator);
997         delete_from_our_metadata_(our_current_position--);
998
999         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1000                 return false;
1001
1002         printf("S[V]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1003         if(!app->set_data(data, 100, true))
1004                 return die_("setting APPLICATION data");
1005         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1006                 return die_("copying object");
1007         delete_from_our_metadata_(our_current_position+1);
1008         if(!iterator.insert_block_after(app, true))
1009                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1010
1011         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1012                 return false;
1013
1014         printf("SV[A]PP\tdelete (middle block), don't replace with padding\n");
1015         if(!iterator.delete_block(false))
1016                 return die_ss_("iterator.delete_block(false)", iterator);
1017         delete_from_our_metadata_(our_current_position--);
1018
1019         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1020                 return false;
1021
1022         printf("S[V]PP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n");
1023         if(!app->set_data(data, 96, true))
1024                 return die_("setting APPLICATION data");
1025         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1026                 return die_("copying object");
1027         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(0);
1028         if(!iterator.insert_block_after(app, true))
1029                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1030
1031         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1032                 return false;
1033
1034         printf("SV[A]PP\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("S[V]PP\tnext\n");
1043         if(!iterator.next())
1044                 return die_("iterator ended early\n");
1045         our_current_position++;
1046
1047         printf("SV[P]P\tdelete (middle block), don't replace with padding\n");
1048         if(!iterator.delete_block(false))
1049                 return die_ss_("iterator.delete_block(false)", iterator);
1050         delete_from_our_metadata_(our_current_position--);
1051
1052         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1053                 return false;
1054
1055         printf("S[V]P\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
1056         if(!app->set_data(data, 1, true))
1057                 return die_("setting APPLICATION data");
1058         if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
1059                 return die_("copying object");
1060         delete_from_our_metadata_(our_current_position+1);
1061         if(!iterator.insert_block_after(app, true))
1062                 return die_ss_("iterator.insert_block_after(app, true)", iterator);
1063
1064         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1065                 return false;
1066         }
1067
1068         delete app;
1069         delete padding;
1070
1071         if(!remove_file_(flacfile_))
1072                 return false;
1073
1074         return true;
1075 }
1076
1077 static bool test_level_2_()
1078 {
1079         FLAC::Metadata::Prototype *block;
1080         FLAC::Metadata::StreamInfo *streaminfo;
1081         FLAC::Metadata::Application *app;
1082         FLAC::Metadata::Padding *padding;
1083         FLAC__byte data[2000];
1084         unsigned our_current_position;
1085
1086         printf("\n\n++++++ testing level 2 interface\n");
1087
1088         printf("generate read-only file\n");
1089
1090         if(!generate_file_())
1091                 return false;
1092
1093         if(!change_stats_(flacfile_, /*read_only=*/true))
1094                 return false;
1095
1096         printf("create chain\n");
1097         FLAC::Metadata::Chain chain;
1098         if(!chain.is_valid())
1099                 return die_("allocating memory for chain");
1100
1101         printf("read chain\n");
1102
1103         if(!chain.read(flacfile_))
1104                 return die_c_("reading chain", chain.status());
1105
1106         printf("[S]VP\ttest initial metadata\n");
1107
1108         if(!compare_chain_(chain, 0, 0))
1109                 return false;
1110         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1111                 return false;
1112
1113         printf("switch file to read-write\n");
1114
1115         if(!change_stats_(flacfile_, /*read-only=*/false))
1116                 return false;
1117
1118         printf("create iterator\n");
1119         {
1120         FLAC::Metadata::Iterator iterator;
1121         if(!iterator.is_valid())
1122                 return die_("allocating memory for iterator");
1123
1124         our_current_position = 0;
1125
1126         iterator.init(chain);
1127
1128         if(0 == (block = iterator.get_block()))
1129                 return die_("getting block from iterator");
1130
1131         FLAC__ASSERT(block->get_type() == FLAC__METADATA_TYPE_STREAMINFO);
1132
1133         printf("[S]VP\tmodify STREAMINFO, write\n");
1134
1135         streaminfo = dynamic_cast<FLAC::Metadata::StreamInfo *>(block);
1136         FLAC__ASSERT(0 != streaminfo);
1137         streaminfo->set_sample_rate(32000);
1138         if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
1139                 return die_("copying object");
1140
1141         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/true))
1142                 return die_c_("during chain.write(false, true)", chain.status());
1143         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1144                 return false;
1145         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1146                 return false;
1147
1148         printf("[S]VP\tnext\n");
1149         if(!iterator.next())
1150                 return die_("iterator ended early\n");
1151         our_current_position++;
1152
1153         printf("S[V]P\tnext\n");
1154         if(!iterator.next())
1155                 return die_("iterator ended early\n");
1156         our_current_position++;
1157
1158         printf("SV[P]\treplace PADDING with identical-size APPLICATION\n");
1159         if(0 == (block = iterator.get_block()))
1160                 return die_("getting block from iterator");
1161         if(0 == (app = new FLAC::Metadata::Application()))
1162                 return die_("new FLAC::Metadata::Application()");
1163         app->set_id((const unsigned char *)"duh");
1164         if(!app->set_data(data, block->get_length()-(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), true))
1165                 return die_("setting APPLICATION data");
1166         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1167                 return die_("copying object");
1168         if(!iterator.set_block(app))
1169                 return die_c_("iterator.set_block(app)", chain.status());
1170
1171         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1172                 return die_c_("during chain.write(false, false)", chain.status());
1173         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1174                 return false;
1175         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1176                 return false;
1177
1178         printf("SV[A]\tshrink APPLICATION, don't use padding\n");
1179         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1180                 return die_("copying object");
1181         if(!app->set_data(data, 26, true))
1182                 return die_("setting APPLICATION data");
1183         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1184                 return die_("copying object");
1185         if(!iterator.set_block(app))
1186                 return die_c_("iterator.set_block(app)", chain.status());
1187
1188         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1189                 return die_c_("during chain.write(false, false)", chain.status());
1190         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1191                 return false;
1192         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1193                 return false;
1194
1195         printf("SV[A]\tgrow APPLICATION, don't use padding\n");
1196         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1197                 return die_("copying object");
1198         if(!app->set_data(data, 28, true))
1199                 return die_("setting APPLICATION data");
1200         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1201                 return die_("copying object");
1202         if(!iterator.set_block(app))
1203                 return die_c_("iterator.set_block(app)", chain.status());
1204
1205         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1206                 return die_c_("during chain.write(false, false)", chain.status());
1207         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1208                 return false;
1209         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1210                 return false;
1211
1212         printf("SV[A]\tgrow APPLICATION, use padding, but last block is not padding\n");
1213         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1214                 return die_("copying object");
1215         if(!app->set_data(data, 36, true))
1216                 return die_("setting APPLICATION data");
1217         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1218                 return die_("copying object");
1219         if(!iterator.set_block(app))
1220                 return die_c_("iterator.set_block(app)", chain.status());
1221
1222         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1223                 return die_c_("during chain.write(false, false)", chain.status());
1224         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1225                 return false;
1226         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1227                 return false;
1228
1229         printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, but delta is too small for new PADDING block\n");
1230         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1231                 return die_("copying object");
1232         if(!app->set_data(data, 33, true))
1233                 return die_("setting APPLICATION data");
1234         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1235                 return die_("copying object");
1236         if(!iterator.set_block(app))
1237                 return die_c_("iterator.set_block(app)", chain.status());
1238
1239         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1240                 return die_c_("during chain.write(true, false)", chain.status());
1241         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1242                 return false;
1243         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1244                 return false;
1245
1246         printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, delta is enough for new PADDING block\n");
1247         if(0 == (padding = new FLAC::Metadata::Padding()))
1248                 return die_("creating PADDING block");
1249         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1250                 return die_("copying object");
1251         if(!app->set_data(data, 29, true))
1252                 return die_("setting APPLICATION data");
1253         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1254                 return die_("copying object");
1255         padding->set_length(0);
1256         if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/false))
1257                 return die_("internal error");
1258         if(!iterator.set_block(app))
1259                 return die_c_("iterator.set_block(app)", chain.status());
1260
1261         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1262                 return die_c_("during chain.write(true, false)", chain.status());
1263         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1264                 return false;
1265         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1266                 return false;
1267
1268         printf("SV[A]P\tshrink APPLICATION, use padding, last block is padding\n");
1269         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1270                 return die_("copying object");
1271         if(!app->set_data(data, 16, true))
1272                 return die_("setting APPLICATION data");
1273         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1274                 return die_("copying object");
1275         dynamic_cast<FLAC::Metadata::Padding *>(our_metadata_.blocks[our_current_position+1])->set_length(13);
1276         if(!iterator.set_block(app))
1277                 return die_c_("iterator.set_block(app)", chain.status());
1278
1279         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1280                 return die_c_("during chain.write(true, false)", chain.status());
1281         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1282                 return false;
1283         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1284                 return false;
1285
1286         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding, but delta is too small\n");
1287         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1288                 return die_("copying object");
1289         if(!app->set_data(data, 50, true))
1290                 return die_("setting APPLICATION data");
1291         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1292                 return die_("copying object");
1293         if(!iterator.set_block(app))
1294                 return die_c_("iterator.set_block(app)", chain.status());
1295
1296         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1297                 return die_c_("during chain.write(true, false)", chain.status());
1298         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1299                 return false;
1300         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1301                 return false;
1302
1303         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exceeding size\n");
1304         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1305                 return die_("copying object");
1306         if(!app->set_data(data, 56, true))
1307                 return die_("setting APPLICATION data");
1308         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1309                 return die_("copying object");
1310         add_to_padding_length_(our_current_position+1, -(56 - 50));
1311         if(!iterator.set_block(app))
1312                 return die_c_("iterator.set_block(app)", chain.status());
1313
1314         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1315                 return die_c_("during chain.write(true, false)", chain.status());
1316         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1317                 return false;
1318         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1319                 return false;
1320
1321         printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exact size\n");
1322         if(0 == (app = dynamic_cast<FLAC::Metadata::Application *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1323                 return die_("copying object");
1324         if(!app->set_data(data, 67, true))
1325                 return die_("setting APPLICATION data");
1326         if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
1327                 return die_("copying object");
1328         delete_from_our_metadata_(our_current_position+1);
1329         if(!iterator.set_block(app))
1330                 return die_c_("iterator.set_block(app)", chain.status());
1331
1332         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1333                 return die_c_("during chain.write(true, false)", chain.status());
1334         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1335                 return false;
1336         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1337                 return false;
1338
1339         printf("SV[A]\tprev\n");
1340         if(!iterator.prev())
1341                 return die_("iterator ended early\n");
1342         our_current_position--;
1343
1344         printf("S[V]A\tprev\n");
1345         if(!iterator.prev())
1346                 return die_("iterator ended early\n");
1347         our_current_position--;
1348
1349         printf("[S]VA\tinsert PADDING before STREAMINFO (should fail)\n");
1350         if(0 == (padding = new FLAC::Metadata::Padding()))
1351                 return die_("creating PADDING block");
1352         padding->set_length(30);
1353         if(!iterator.insert_block_before(padding))
1354                 printf("\titerator.insert_block_before() returned false like it should\n");
1355         else
1356                 return die_("iterator.insert_block_before() should have returned false");
1357
1358         printf("[S]VA\tnext\n");
1359         if(!iterator.next())
1360                 return die_("iterator ended early\n");
1361         our_current_position++;
1362
1363         printf("S[V]A\tinsert PADDING after\n");
1364         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1365                 return die_("copying metadata");
1366         if(!iterator.insert_block_after(padding))
1367                 return die_("iterator.insert_block_after(padding)");
1368
1369         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1370                 return false;
1371
1372         printf("SV[P]A\tinsert PADDING before\n");
1373         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1374                 return die_("creating PADDING block");
1375         padding->set_length(17);
1376         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1377                 return die_("copying metadata");
1378         if(!iterator.insert_block_before(padding))
1379                 return die_("iterator.insert_block_before(padding)");
1380
1381         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1382                 return false;
1383
1384         printf("SV[P]PA\tinsert PADDING before\n");
1385         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[our_current_position]))))
1386                 return die_("creating PADDING block");
1387         padding->set_length(0);
1388         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1389                 return die_("copying metadata");
1390         if(!iterator.insert_block_before(padding))
1391                 return die_("iterator.insert_block_before(padding)");
1392
1393         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1394                 return false;
1395
1396         printf("SV[P]PPA\tnext\n");
1397         if(!iterator.next())
1398                 return die_("iterator ended early\n");
1399         our_current_position++;
1400
1401         printf("SVP[P]PA\tnext\n");
1402         if(!iterator.next())
1403                 return die_("iterator ended early\n");
1404         our_current_position++;
1405
1406         printf("SVPP[P]A\tnext\n");
1407         if(!iterator.next())
1408                 return die_("iterator ended early\n");
1409         our_current_position++;
1410
1411         printf("SVPPP[A]\tinsert PADDING after\n");
1412         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1413                 return die_("creating PADDING block");
1414         padding->set_length(57);
1415         if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
1416                 return die_("copying metadata");
1417         if(!iterator.insert_block_after(padding))
1418                 return die_("iterator.insert_block_after(padding)");
1419
1420         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1421                 return false;
1422
1423         printf("SVPPPA[P]\tinsert PADDING before\n");
1424         if(0 == (padding = dynamic_cast<FLAC::Metadata::Padding *>(FLAC::Metadata::clone(our_metadata_.blocks[2]))))
1425                 return die_("creating PADDING block");
1426         padding->set_length(99);
1427         if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true))
1428                 return die_("copying metadata");
1429         if(!iterator.insert_block_before(padding))
1430                 return die_("iterator.insert_block_before(padding)");
1431
1432         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1433                 return false;
1434
1435         }
1436         our_current_position = 0;
1437
1438         printf("SVPPPAPP\tmerge padding\n");
1439         chain.merge_padding();
1440         add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[3]->get_length());
1441         add_to_padding_length_(2, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[4]->get_length());
1442         add_to_padding_length_(6, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[7]->get_length());
1443         delete_from_our_metadata_(7);
1444         delete_from_our_metadata_(4);
1445         delete_from_our_metadata_(3);
1446
1447         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1448                 return die_c_("during chain.write(true, false)", chain.status());
1449         if(!compare_chain_(chain, 0, 0))
1450                 return false;
1451         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1452                 return false;
1453
1454         printf("SVPAP\tsort padding\n");
1455         chain.sort_padding();
1456         add_to_padding_length_(4, FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[2]->get_length());
1457         delete_from_our_metadata_(2);
1458
1459         if(!chain.write(/*use_padding=*/true, /*preserve_file_stats=*/false))
1460                 return die_c_("during chain.write(true, false)", chain.status());
1461         if(!compare_chain_(chain, 0, 0))
1462                 return false;
1463         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1464                 return false;
1465
1466         printf("create iterator\n");
1467         {
1468         FLAC::Metadata::Iterator iterator;
1469         if(!iterator.is_valid())
1470                 return die_("allocating memory for iterator");
1471
1472         our_current_position = 0;
1473
1474         iterator.init(chain);
1475
1476         printf("[S]VAP\tnext\n");
1477         if(!iterator.next())
1478                 return die_("iterator ended early\n");
1479         our_current_position++;
1480
1481         printf("S[V]AP\tnext\n");
1482         if(!iterator.next())
1483                 return die_("iterator ended early\n");
1484         our_current_position++;
1485
1486         printf("SV[A]P\tdelete middle block, replace with padding\n");
1487         if(0 == (padding = new FLAC::Metadata::Padding()))
1488                 return die_("creating PADDING block");
1489         padding->set_length(71);
1490         if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1491                 return die_("copying object");
1492         if(!iterator.delete_block(/*replace_with_padding=*/true))
1493                 return die_c_("iterator.delete_block(true)", chain.status());
1494
1495         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1496                 return false;
1497
1498         printf("S[V]PP\tnext\n");
1499         if(!iterator.next())
1500                 return die_("iterator ended early\n");
1501         our_current_position++;
1502
1503         printf("SV[P]P\tdelete middle block, don't replace with padding\n");
1504         delete_from_our_metadata_(our_current_position--);
1505         if(!iterator.delete_block(/*replace_with_padding=*/false))
1506                 return die_c_("iterator.delete_block(false)", chain.status());
1507
1508         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1509                 return false;
1510
1511         printf("S[V]P\tnext\n");
1512         if(!iterator.next())
1513                 return die_("iterator ended early\n");
1514         our_current_position++;
1515
1516         printf("SV[P]\tdelete last block, replace with padding\n");
1517         if(0 == (padding = new FLAC::Metadata::Padding()))
1518                 return die_("creating PADDING block");
1519         padding->set_length(219);
1520         if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false))
1521                 return die_("copying object");
1522         if(!iterator.delete_block(/*replace_with_padding=*/true))
1523                 return die_c_("iterator.delete_block(true)", chain.status());
1524
1525         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1526                 return false;
1527
1528         printf("S[V]P\tnext\n");
1529         if(!iterator.next())
1530                 return die_("iterator ended early\n");
1531         our_current_position++;
1532
1533         printf("SV[P]\tdelete last block, don't replace with padding\n");
1534         delete_from_our_metadata_(our_current_position--);
1535         if(!iterator.delete_block(/*replace_with_padding=*/false))
1536                 return die_c_("iterator.delete_block(false)", chain.status());
1537
1538         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1539                 return false;
1540
1541         printf("S[V]\tprev\n");
1542         if(!iterator.prev())
1543                 return die_("iterator ended early\n");
1544         our_current_position--;
1545
1546         printf("[S]V\tdelete STREAMINFO block, should fail\n");
1547         if(iterator.delete_block(/*replace_with_padding=*/false))
1548                 return die_("iterator.delete_block() on STREAMINFO should have failed but didn't");
1549
1550         if(!compare_chain_(chain, our_current_position, iterator.get_block()))
1551                 return false;
1552
1553         }
1554         our_current_position = 0;
1555
1556         printf("SV\tmerge padding\n");
1557         chain.merge_padding();
1558
1559         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1560                 return die_c_("during chain.write(false, false)", chain.status());
1561         if(!compare_chain_(chain, 0, 0))
1562                 return false;
1563         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1564                 return false;
1565
1566         printf("SV\tsort padding\n");
1567         chain.sort_padding();
1568
1569         if(!chain.write(/*use_padding=*/false, /*preserve_file_stats=*/false))
1570                 return die_c_("during chain.write(false, false)", chain.status());
1571         if(!compare_chain_(chain, 0, 0))
1572                 return false;
1573         if(!test_file_(flacfile_, /*ignore_metadata=*/false))
1574                 return false;
1575
1576         if(!remove_file_(flacfile_))
1577                 return false;
1578
1579         return true;
1580 }
1581
1582 bool test_metadata_file_manipulation()
1583 {
1584         printf("\n+++ libFLAC++ unit test: metadata manipulation\n\n");
1585
1586         our_metadata_.num_blocks = 0;
1587
1588         if(!test_level_0_())
1589                 return false;
1590
1591         if(!test_level_1_())
1592                 return false;
1593
1594         if(!test_level_2_())
1595                 return false;
1596
1597         return true;
1598 }