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