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