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