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