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