finished level 1 tests, started level 2 tests
authorJosh Coalson <jcoalson@users.sourceforce.net>
Sat, 11 May 2002 05:41:42 +0000 (05:41 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Sat, 11 May 2002 05:41:42 +0000 (05:41 +0000)
src/test_unit/metadata_manip.c

index 75b5d5d..ea5edd1 100644 (file)
 #include <unistd.h> /* for unlink() */
 #endif
 
+/******************************************************************************
+       The general strategy of these tests (for interface levels 1 and 2) is
+       to create a dummy FLAC file with a known set of initial metadata
+       blocks, then keep a mirror locally of what we expect the metadata to be
+       after each operation.  Then testing becomes a simple matter of running
+       a FLAC__FileDecoder over the dummy file after each operation, comparing
+       the decoded metadata to what's in our local copy.  If there are any
+       differences in the metadata,  or the actual audio data is corrupted, we
+       will catch it while decoding.
+******************************************************************************/
+
 typedef struct {
        FLAC__bool error_occurred;
 } decoder_client_struct;
@@ -47,8 +58,12 @@ typedef struct {
 } our_metadata_struct;
 
 static const char *flacfile_ = "metadata.flac";
+
+/* our copy of the metadata in flacfile_ */
 static our_metadata_struct our_metadata_;
-static unsigned mc_our_blocknumber_ = 0;
+
+/* the current block number that corresponds to the position of the iterator we are testing */
+static unsigned mc_our_block_number_ = 0;
 
 static FLAC__bool die_(const char *msg)
 {
@@ -56,6 +71,13 @@ static FLAC__bool die_(const char *msg)
        return false;
 }
 
+static FLAC__bool die_c_(const char *msg, FLAC__MetaData_ChainStatus status)
+{
+       printf("ERROR: %s\n", msg);
+       printf("       status=%s\n", FLAC__MetaData_ChainStatusString[status]);
+       return false;
+}
+
 static FLAC__bool die_ss_(const char *msg, FLAC__MetaData_SimpleIterator *siterator)
 {
        printf("ERROR: %s\n", msg);
@@ -63,6 +85,8 @@ static FLAC__bool die_ss_(const char *msg, FLAC__MetaData_SimpleIterator *sitera
        return false;
 }
 
+/* functions for working with our metadata copy */
+
 static FLAC__bool replace_in_our_metadata_(FLAC__StreamMetaData *block, unsigned position, FLAC__bool copy)
 {
        unsigned i;
@@ -126,9 +150,62 @@ static void delete_from_our_metadata_(unsigned position)
        }
 }
 
+/* function for comparing our metadata to a FLAC__MetaData_Chain */
+
+static FLAC__bool compare_chain_(FLAC__MetaData_Chain *chain)
+{
+       unsigned i;
+       FLAC__MetaData_Iterator *iterator;
+       FLAC__StreamMetaData *block;
+       FLAC__bool next_ok = true;
+
+       FLAC__ASSERT(0 != chain);
+
+       printf("\tcomparing chain... ");
+       fflush(stdout);
+
+       if(0 == (iterator = FLAC__metadata_iterator_new()))
+               return die_("allocating memory for iterator");
+
+       FLAC__metadata_iterator_init(iterator, chain);
+
+       i = 0;
+       do {
+               printf("%u... ", i);
+               fflush(stdout);
+
+               if(0 == (block = FLAC__metadata_iterator_get_block(iterator))) {
+                       FLAC__metadata_iterator_delete(iterator);
+                       return die_("getting block from iterator");
+               }
+
+               if(!compare_block_(our_metadata_.blocks[i], block)) {
+                       FLAC__metadata_iterator_delete(iterator);
+                       return die_("metadata block mismatch");
+               }
+
+               i++;
+               next_ok = FLAC__metadata_iterator_next(iterator);
+       } while(i < our_metadata_.num_blocks && next_ok);
+
+       FLAC__metadata_iterator_delete(iterator);
+
+       if(next_ok)
+               return die_("chain has more blocks than expected");
+
+       if(i < our_metadata_.num_blocks)
+               return die_("short block count in chain");
+
+       printf("PASSED\n");
+
+       return true;
+}
+
+/* decoder callbacks for checking the file */
+
 static FLAC__StreamDecoderWriteStatus decoder_write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
 {
-       (void)decoder, (void)frame, (void)buffer, (void)client_data;
+       (void)decoder, (void)buffer, (void)client_data;
 
        if(
                (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER && frame->header.number.frame_number == 0) ||
@@ -150,6 +227,44 @@ static void decoder_error_callback_(const FLAC__FileDecoder *decoder, FLAC__Stre
        printf("ERROR: got error callback, status = %s (%u)\n", FLAC__StreamDecoderErrorStatusString[status], (unsigned)status);
 }
 
+/* this version pays no attention to the metadata */
+static void decoder_metadata_callback_null_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
+{
+       (void)decoder, (void)metadata, (void)client_data;
+
+       printf("%d... ", mc_our_block_number_);
+       fflush(stdout);
+
+       mc_our_block_number_++;
+}
+
+/* this version is used when we want to compare to our metadata copy */
+static void decoder_metadata_callback_compare_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
+{
+       decoder_client_struct *dcd = (decoder_client_struct*)client_data;
+
+       (void)decoder;
+
+       /* don't bother checking if we've already hit an error */
+       if(dcd->error_occurred)
+               return;
+
+       printf("%d... ", mc_our_block_number_);
+       fflush(stdout);
+
+       if(mc_our_block_number_ >= our_metadata_.num_blocks) {
+               (void)die_("got more metadata blocks than expected");
+               dcd->error_occurred = true;
+       }
+       else {
+               if(!compare_block_(our_metadata_.blocks[mc_our_block_number_], metadata)) {
+                       (void)die_("metadata block mismatch");
+                       dcd->error_occurred = true;
+               }
+       }
+       mc_our_block_number_++;
+}
+
 static FLAC__StreamEncoderWriteStatus encoder_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data)
 {
        encoder_client_struct *ecd = (encoder_client_struct*)client_data;
@@ -170,6 +285,7 @@ static void encoder_metadata_callback_(const FLAC__StreamEncoder *encoder, const
 static FLAC__bool generate_file_(const char *input_filename)
 {
        FLAC__StreamEncoder *encoder;
+       FLAC__StreamMetaData streaminfo, padding;
        encoder_client_struct encoder_client_data;
        FILE *file;
        FLAC__byte buffer[4096];
@@ -178,10 +294,31 @@ static FLAC__bool generate_file_(const char *input_filename)
 
        FLAC__ASSERT(0 != input_filename);
 
-       our_metadata_.num_blocks = 0;
-
        printf("generating FLAC file for test\n");
 
+       while(our_metadata_.num_blocks > 0)
+               delete_from_our_metadata_(0);
+
+       streaminfo.is_last = false;
+       streaminfo.type = FLAC__METADATA_TYPE_STREAMINFO;
+       streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
+       streaminfo.data.stream_info.min_blocksize = 576;
+       streaminfo.data.stream_info.max_blocksize = 576;
+       streaminfo.data.stream_info.min_framesize = 0;
+       streaminfo.data.stream_info.max_framesize = 0;
+       streaminfo.data.stream_info.sample_rate = 44100;
+       streaminfo.data.stream_info.channels = 1;
+       streaminfo.data.stream_info.bits_per_sample = 8;
+       streaminfo.data.stream_info.total_samples = 0;
+       memset(streaminfo.data.stream_info.md5sum, 0, 16);
+
+       padding.is_last = true;
+       padding.type = FLAC__METADATA_TYPE_PADDING;
+       padding.length = 1234;
+
+       if(!insert_to_our_metadata_(&streaminfo, 0, /*copy=*/true) || !insert_to_our_metadata_(&padding, 1, /*copy=*/true))
+               return die_("priming our metadata");
+
        if(0 == (file = fopen(input_filename, "rb")))
                return die_("opening input file");
        if(0 == (encoder_client_data.file = fopen(flacfile_, "wb"))) {
@@ -199,10 +336,10 @@ static FLAC__bool generate_file_(const char *input_filename)
        FLAC__stream_encoder_set_streamable_subset(encoder, true);
        FLAC__stream_encoder_set_do_mid_side_stereo(encoder, false);
        FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, false);
-       FLAC__stream_encoder_set_channels(encoder, 1);
-       FLAC__stream_encoder_set_bits_per_sample(encoder, 8);
-       FLAC__stream_encoder_set_sample_rate(encoder, 44100);
-       FLAC__stream_encoder_set_blocksize(encoder, 576);
+       FLAC__stream_encoder_set_channels(encoder, streaminfo.data.stream_info.channels);
+       FLAC__stream_encoder_set_bits_per_sample(encoder, streaminfo.data.stream_info.bits_per_sample);
+       FLAC__stream_encoder_set_sample_rate(encoder, streaminfo.data.stream_info.sample_rate);
+       FLAC__stream_encoder_set_blocksize(encoder, streaminfo.data.stream_info.min_blocksize);
        FLAC__stream_encoder_set_max_lpc_order(encoder, 0);
        FLAC__stream_encoder_set_qlp_coeff_precision(encoder, 0);
        FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, false);
@@ -211,9 +348,9 @@ static FLAC__bool generate_file_(const char *input_filename)
        FLAC__stream_encoder_set_min_residual_partition_order(encoder, 0);
        FLAC__stream_encoder_set_max_residual_partition_order(encoder, 0);
        FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, 0);
-       FLAC__stream_encoder_set_total_samples_estimate(encoder, 0);
+       FLAC__stream_encoder_set_total_samples_estimate(encoder, streaminfo.data.stream_info.total_samples);
        FLAC__stream_encoder_set_seek_table(encoder, 0);
-       FLAC__stream_encoder_set_padding(encoder, 12345);
+       FLAC__stream_encoder_set_padding(encoder, padding.length);
        FLAC__stream_encoder_set_last_metadata_is_last(encoder, true);
        FLAC__stream_encoder_set_write_callback(encoder, encoder_write_callback_);
        FLAC__stream_encoder_set_metadata_callback(encoder, encoder_metadata_callback_);
@@ -263,7 +400,7 @@ static FLAC__bool test_file_(const char *filename, void (*metadata_callback)(con
        FLAC__ASSERT(0 != filename);
        FLAC__ASSERT(0 != metadata_callback);
 
-       mc_our_blocknumber_ = 0;
+       mc_our_block_number_ = 0;
        decoder_client_data.error_occurred = false;
 
        printf("\ttesting '%s'... ", filename);
@@ -296,7 +433,7 @@ static FLAC__bool test_file_(const char *filename, void (*metadata_callback)(con
        if(decoder_client_data.error_occurred)
                return false;
 
-       if(mc_our_blocknumber_ != our_metadata_.num_blocks)
+       if(mc_our_block_number_ != our_metadata_.num_blocks)
                return die_("short metadata block count");
 
        printf("PASSED\n");
@@ -329,43 +466,15 @@ static FLAC__bool change_stats_(const char *filename, FLAC__bool read_only)
 
 static FLAC__bool remove_file_(const char *filename)
 {
+       while(our_metadata_.num_blocks > 0)
+               delete_from_our_metadata_(0);
+
        if(!change_stats_(filename, /*read_only=*/false) || 0 != unlink(filename))
                return die_("removing file");
 
        return true;
 }
 
-static void mc_null_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
-{
-       (void)decoder, (void)metadata, (void)client_data;
-}
-
-static void mc_ours_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
-{
-       decoder_client_struct *dcd = (decoder_client_struct*)client_data;
-
-       (void)decoder;
-
-       /* don't bother checking if we've already hit an error */
-       if(dcd->error_occurred)
-               return;
-
-       printf("%d... ", mc_our_blocknumber_);
-       fflush(stdout);
-
-       if(mc_our_blocknumber_ >= our_metadata_.num_blocks) {
-               (void)die_("got more metadata blocks than expected");
-               dcd->error_occurred = true;
-       }
-       else {
-               if(!compare_block_(our_metadata_.blocks[mc_our_blocknumber_], metadata)) {
-                       (void)die_("metadata block mismatch");
-                       dcd->error_occurred = true;
-               }
-       }
-       mc_our_blocknumber_++;
-}
-
 static FLAC__bool test_level_0_(const char *progname)
 {
        FLAC__StreamMetaData_StreamInfo streaminfo;
@@ -375,7 +484,7 @@ static FLAC__bool test_level_0_(const char *progname)
        if(!generate_file_(progname))
                return false;
 
-       if(!test_file_(flacfile_, mc_null_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_null_))
                return false;
 
        if(!FLAC__metadata_get_streaminfo(flacfile_, &streaminfo))
@@ -402,11 +511,9 @@ static FLAC__bool test_level_0_(const char *progname)
 static FLAC__bool test_level_1_(const char *progname)
 {
        FLAC__MetaData_SimpleIterator *siterator;
-       FLAC__MetaData_Iterator *iterator;
-       FLAC__MetaData_Chain *chain;
        FLAC__StreamMetaData *block, *app, *padding;
        FLAC__byte data[1000];
-       unsigned i = 0, our_current_position = 0;
+       unsigned our_current_position = 0;
 
        printf("\n\n++++++ testing level 1 interface\n");
 
@@ -420,7 +527,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!change_stats_(flacfile_, /*read_only=*/true))
                return false;
 
-       if(!test_file_(flacfile_, mc_null_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_null_))
                return false;
 
        if(0 == (siterator = FLAC__metadata_simple_iterator_new()))
@@ -456,7 +563,6 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_("mismatch in min_blocksize");
        if(block->data.stream_info.max_blocksize != 576)
                return die_("mismatch in max_blocksize");
-       (void)insert_to_our_metadata_(block, our_current_position, /*copy=*/false);
 
        if(!FLAC__metadata_simple_iterator_next(siterator))
                return die_("forward iterator ended early");
@@ -471,9 +577,8 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!block->is_last)
                return die_("expected is_last to be true");
        /* check to see if some basic data matches (c.f. generate_file_()) */
-       if(block->length != 12345)
+       if(block->length != 1234)
                return die_("bad STREAMINFO length");
-       (void)insert_to_our_metadata_(block, our_current_position, /*copy=*/false);
 
        if(FLAC__metadata_simple_iterator_next(siterator))
                return die_("forward iterator returned true but should have returned false");
@@ -486,10 +591,8 @@ static FLAC__bool test_level_1_(const char *progname)
 
        printf("testing FLAC__metadata_simple_iterator_set_block() on read-only file...\n");
 
-       if(!FLAC__metadata_simple_iterator_set_block(siterator, (void*)99, false)) {
+       if(!FLAC__metadata_simple_iterator_set_block(siterator, (void*)99, false))
                printf("PASSED.  FLAC__metadata_simple_iterator_set_block() returned false like it should\n");
-               printf("  status=%s\n", FLAC__MetaData_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(siterator)]);
-       }
        else
                return die_("FLAC__metadata_simple_iterator_set_block() returned true but shouldn't have");
 
@@ -506,7 +609,7 @@ static FLAC__bool test_level_1_(const char *progname)
 
        if(0 == (app = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)))
                return die_("FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)");
-       memcpy(app->data.application.id, "duh", 4);
+       memcpy(app->data.application.id, "duh", (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8));
 
        printf("creating PADDING block\n");
 
@@ -524,10 +627,8 @@ static FLAC__bool test_level_1_(const char *progname)
        printf("is writable = %u\n", (unsigned)FLAC__metadata_simple_iterator_is_writable(siterator));
 
        printf("[S]P\ttry to write over STREAMINFO block...\n");
-       if(!FLAC__metadata_simple_iterator_set_block(siterator, app, false)) {
+       if(!FLAC__metadata_simple_iterator_set_block(siterator, app, false))
                printf("FLAC__metadata_simple_iterator_set_block() returned false like it should\n");
-               printf("  status=%s\n", FLAC__MetaData_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(siterator)]);
-       }
        else
                return die_("FLAC__metadata_simple_iterator_set_block() returned true but shouldn't have");
 
@@ -560,7 +661,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
                return false;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
        
        printf("S[P]PP\tprev\n");
@@ -572,7 +673,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(FLAC__metadata_simple_iterator_delete_block(siterator, false))
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false) should have returned false", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("[S]PPP\tnext\n");
@@ -595,7 +696,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("[S]PP\tnext\n");
@@ -613,7 +714,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        our_current_position--;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[P]P\tnext\n");
@@ -626,7 +727,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[P]\tprev\n");
@@ -644,7 +745,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, block, false)", siterator);
        FLAC__metadata_object_delete(block);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("[S]P\tinsert APPLICATION after, expand into padding of exceeding size\n");
@@ -653,9 +754,9 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
        if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
                return false;
-       our_metadata_.blocks[our_current_position+1]->length -= 4 + app->length;
+       our_metadata_.blocks[our_current_position+1]->length -= (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + app->length;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]P\tnext\n");
@@ -669,9 +770,9 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
        if(!insert_to_our_metadata_(app, our_current_position, /*copy=*/true))
                return false;
-       our_metadata_.blocks[our_current_position+1]->length -= 4 + app->length;
+       our_metadata_.blocks[our_current_position+1]->length -= (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + app->length;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]P\tset APPLICATION (grow), don't expand into padding\n");
@@ -683,7 +784,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, false))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, false)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]P\tset APPLICATION (shrink), don't fill in with padding\n");
@@ -695,7 +796,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, false))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, false)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]P\tset APPLICATION (grow), expand into padding of exceeding size\n");
@@ -708,7 +809,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]P\tset APPLICATION (shrink), fill in with padding\n");
@@ -723,7 +824,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]PP\tnext\n");
@@ -743,7 +844,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, padding, false))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, padding, false)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SAAP[P]\tset APPLICATION (grow)\n");
@@ -753,7 +854,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, false))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, false)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SAAP[A]\tset PADDING (equal)\n");
@@ -763,7 +864,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, padding, false))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, padding, false)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SAAP[P]\tprev\n");
@@ -776,7 +877,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SA[A]P\tdelete (middle block), don't replace with padding\n");
@@ -784,7 +885,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]P\tnext\n");
@@ -799,7 +900,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
                return false;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SAP[P]\tprev\n");
@@ -820,7 +921,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]PP\tset APPLICATION (grow), try to expand into padding which is 'close' but still too small\n");
@@ -831,7 +932,19 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
+               return false;
+
+       printf("S[A]PP\tset APPLICATION (grow), expand into padding which will leave 0-length pad\n");
+       if(!FLAC__metadata_object_application_set_data(app, data, 87, true))
+               return die_("setting APPLICATION data");
+       if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true))
+               return die_("copying object");
+       our_metadata_.blocks[our_current_position+1]->length = 0;
+       if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
+               return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
+
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]PP\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
@@ -843,7 +956,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]P\tset APPLICATION (grow), expand into padding which is exactly consumed\n");
@@ -856,7 +969,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]\tset PADDING (equal size)\n");
@@ -866,7 +979,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_set_block(siterator, padding, true))
                return die_ss_("FLAC__metadata_simple_iterator_set_block(siterator, padding, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[P]\tinsert PADDING after\n");
@@ -875,7 +988,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
                return false;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SP[P]\tinsert PADDING after\n");
@@ -885,7 +998,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true))
                return false;
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("SPP[P]\tprev\n");
@@ -911,7 +1024,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]PPP\tdelete (middle block), don't replace with padding\n");
@@ -919,7 +1032,7 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("[S]PPP\tinsert APPLICATION after, try to expand into padding which is 'close' but still too small\n");
@@ -930,7 +1043,7 @@ static FLAC__bool test_level_1_(const char *progname)
        if(!FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("S[A]PPP\tdelete (middle block), don't replace with padding\n");
@@ -938,61 +1051,76 @@ static FLAC__bool test_level_1_(const char *progname)
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
        printf("[S]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
        if(!FLAC__metadata_object_application_set_data(app, data, 100, true))
                return die_("setting APPLICATION data");
-       if(!insert_to_our_metadata_(app,++our_current_position, /*copy=*/true))
+       if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
                return die_("copying object");
        delete_from_our_metadata_(our_current_position+1);
        if(!FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
-       printf("S[A]PPP\tdelete (middle block), don't replace with padding\n");
+       printf("S[A]PP\tdelete (middle block), don't replace with padding\n");
        if(!FLAC__metadata_simple_iterator_delete_block(siterator, false))
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
-       printf("[S]PPP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n");
+       printf("[S]PP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n");
        if(!FLAC__metadata_object_application_set_data(app, data, 96, true))
                return die_("setting APPLICATION data");
-       if(!insert_to_our_metadata_(app,++our_current_position, /*copy=*/true))
+       if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
                return die_("copying object");
        our_metadata_.blocks[our_current_position+1]->length = 0;
        if(!FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
-       printf("S[A]PPP\tdelete (middle block), don't replace with padding\n");
+       printf("S[A]PP\tdelete (middle block), don't replace with padding\n");
        if(!FLAC__metadata_simple_iterator_delete_block(siterator, false))
                return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
        delete_from_our_metadata_(our_current_position--);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
-       printf("[S]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
-       if(!FLAC__metadata_object_application_set_data(app, data, 100, true))
+       printf("[S]PP\tnext\n");
+       if(!FLAC__metadata_simple_iterator_next(siterator))
+               return die_("iterator ended early\n");
+       our_current_position++;
+
+       printf("S[P]P\tdelete (middle block), don't replace with padding\n");
+       if(!FLAC__metadata_simple_iterator_delete_block(siterator, false))
+               return die_ss_("FLAC__metadata_simple_iterator_delete_block(siterator, false)", siterator);
+       delete_from_our_metadata_(our_current_position--);
+
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
+               return false;
+
+       printf("[S]P\tinsert APPLICATION after, expand into padding which is exactly consumed\n");
+       if(!FLAC__metadata_object_application_set_data(app, data, 1, true))
                return die_("setting APPLICATION data");
-       if(!insert_to_our_metadata_(app,++our_current_position, /*copy=*/true))
+       if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true))
                return die_("copying object");
        delete_from_our_metadata_(our_current_position+1);
        if(!FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true))
                return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(siterator, app, true)", siterator);
 
-       if(!test_file_(flacfile_, mc_ours_))
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
                return false;
 
+       printf("delete simple iterator\n");
+
        FLAC__metadata_simple_iterator_delete(siterator);
 
        FLAC__metadata_object_delete(app);
@@ -1006,416 +1134,123 @@ static FLAC__bool test_level_1_(const char *progname)
 
 static FLAC__bool test_level_2_(const char *progname)
 {
+       FLAC__MetaData_Iterator *iterator;
+       FLAC__MetaData_Chain *chain;
+       FLAC__StreamMetaData *block, *app;
+       FLAC__byte data[2000];
+       unsigned our_current_position;
+
        printf("\n\n++++++ testing level 2 interface\n");
 
-       return true;
-}
+       printf("generate read-only file\n");
 
-int test_metadata_file_manipulation(const char *progname)
-{
-       printf("\n+++ unit test: metadata manipulation\n\n");
+       if(!generate_file_(progname))
+               return false;
 
-       if(!test_level_0_(progname))
-               return 1;
+       if(!change_stats_(flacfile_, /*read_only=*/true))
+               return false;
 
-       if(!test_level_1_(progname))
-               return 1;
+       printf("create chain\n");
 
-       if(!test_level_2_(progname))
-               return 1;
+       if(0 == (chain = FLAC__metadata_chain_new()))
+               return die_("allocating chain");
 
-       return 0;
-}
-#if 0
-static void printb(const FLAC__StreamMetaData *block)
-{
-       printf("\ttype=%s (%u)\n", FLAC__MetaDataTypeString[block->type], (unsigned)block->type);
-       printf("\tis_last=%s (%u)\n", block->is_last? "true":"false", (unsigned)block->is_last);
-       printf("\tlength=%u\n", block->length);
-}
+       printf("read chain\n");
 
-static void printsi(FLAC__MetaData_SimpleIterator *siterator)
-{
-       FLAC__StreamMetaData *block = FLAC__metadata_simple_iterator_get_block(siterator);
-       printb(block);
-       FLAC__metadata_object_delete(block);
-}
+       if(!FLAC__metadata_chain_read(chain, flacfile_))
+               return die_c_("reading chain", FLAC__metadata_chain_status(chain));
 
-int test_metadata()
-{
+       printf("[S]P\ttest initial metadata\n");
 
-       return 0;
-}
-#endif
-#if 0
+       if(!compare_chain_(chain))
+               return false;
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
+               return false;
 
-/***********************************************************************
- * level 1
- * ---------------------------------------------------------------------
- * The general usage of this interface is:
- *
- * Create an iterator using FLAC__metadata_simple_iterator_new()
- * Attach it to a file using FLAC__metadata_simple_iterator_init() and check
- *    the exit code.  Call FLAC__metadata_simple_iterator_is_writable() to
- *    see if the file is writable, or read-only access is allowed.
- * Use _next() and _prev() to move around the blocks.  This is does not
- *    read the actual blocks themselves.  _next() is relatively fast.
- *    _prev() is slower since it needs to search forward from the front
- *    of the file.
- * Use _get_block_type() or _get_block() to access the actual data.  The
- *    returned object is yours to modify and free.
- * Use _set_block() to write a modified block back.  You must have write
- *    permission to the original file.  Make sure to read the whole
- *    comment to _set_block() below.
- * Use _insert_block_after() to add new blocks.  Use the object creation
- *    functions from the end of this header file to generate new objects.
- * Use _delete_block() to remove the block currently referred to by the
- *    iterator, or replace it with padding.
- * Destroy the iterator with FLAC__metadata_simple_iterator_delete()
- *    when finished.
- *
- * NOTE: The FLAC file remains open the whole time between _init() and
- *       _delete(), so make sure you are not altering the file during
- *       this time.
- *
- * NOTE: Do not modify the is_last, length, or type fields of returned
- *       FLAC__MetaDataType objects.  These are managed automatically.
- *
- * NOTE: If any of the modification functions (_set_block, _delete_block,
- *       _insert_block_after, etc) return false, you should delete the
- *       iterator as it may no longer be valid.
- */
+       printf("switch file to read-write\n");
 
-/*
- * Write a block back to the FLAC file.  This function tries to be
- * as efficient as possible; how the block is actually written is
- * shown by the following:
- *
- * Existing block is a STREAMINFO block and the new block is a
- * STREAMINFO block: the new block is written in place.  Make sure
- * you know what you're doing when changing the values of a
- * STREAMINFO block.
- * 
- * Existing block is a STREAMINFO block and the new block is a
- * not a STREAMINFO block: this is an error since the first block
- * must be a STREAMINFO block.  Returns false without altering the
- * file. 
- * 
- * Existing block is not a STREAMINFO block and the new block is a
- * STREAMINFO block: this is an error since there may be only one
- * STREAMINFO block.  Returns false without altering the file. 
- * 
- * Existing block and new block are the same length: the existing
- * block will be replaced by the new block, written in place.
- *
- * Existing block is longer than new block: if use_padding is true,
- * the existing block will be overwritten in place with the new
- * block followed by a PADDING block, if possible, to make the total
- * size the same as the existing block.  Remember that a padding
- * block requires at least four bytes so if the difference in size
- * between the new block and existing block is less than that, the
- * entire file will have to be rewritten, using the new block's
- * exact size.  If use_padding is false, the entire file will be
- * rewritten, replacing the existing block by the new block.
- *
- * Existing block is shorter than new block: if use_padding is true,
- * the function will try and expand the new block into the following
- * PADDING block, if it exists and doing so won't shrink the PADDING
- * block to less than 4 bytes.  If there is no following PADDING
- * block, or it will shrink to less than 4 bytes, or use_padding is
- * false, the entire file is rewritten, replacing the existing block
- * with the new block.  Note that in this case any following PADDING
- * block is preserved as is.
- *
- * After writing the block, the iterator will remain in the same
- * place, i.e. pointing to the new block.
- */
-FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding);
-
-/*
- * This is similar to FLAC__metadata_simple_iterator_set_block()
- * except that instead of writing over an existing block, it appends
- * a block after the existing block.  'use_padding' is again used to
- * tell the function to try an expand into following padding in an
- * attempt to avoid rewriting the entire file.
- *
- * This function will fail and return false if given a STREAMINFO
- * block.
- *
- * After writing the block, the iterator will be pointing to the
- * new block.
- */
-FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding);
-
-/*
- * Deletes the block at the current position.  This will cause the
- * entire FLAC file to be rewritten, unless 'use_padding' is true,
- * in which case the block will be replaced by an equal-sized PADDING
- * block.  The iterator will be left pointing to the block before the
- * one just deleted.
- *
- * You may not delete the STREAMINFO block.
- */
-FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__bool use_padding);
+       if(!change_stats_(flacfile_, /*read-only=*/false))
+               return false;
 
+       printf("[S]P\tmodify STREAMINFO, write\n");
 
-/***********************************************************************
- * level 2
- * ---------------------------------------------------------------------
- * The general usage of this interface is:
- *
- * Create a new chain using FLAC__metadata_chain_new().  A chain is a
- *    linked list of metadata blocks.
- * Read all metadata into the the chain from a FLAC file using
- *    FLAC__metadata_chain_read() and check the status.
- * Optionally, consolidate the padding using
- *    FLAC__metadata_chain_merge_padding() or
- *    FLAC__metadata_chain_sort_padding().
- * Create a new iterator using FLAC__metadata_iterator_new()
- * Initialize the iterator to point to the first element in the chain
- *    using FLAC__metadata_iterator_init()
- * Traverse the chain using FLAC__metadata_iterator_next/prev().
- * Get a block for reading or modification using
- *    FLAC__metadata_iterator_get_block().  The pointer to the object
- *    inside the chain is returned, so the block is yours to modify.
- *    Changes will be reflected in the FLAC file when you write the
- *    chain.  You can also add and delete blocks (see functions below).
- * When done, write out the chain using FLAC__metadata_chain_write().
- *    Make sure to read the whole comment to the function below.
- * Delete the chain using FLAC__metadata_chain_delete().
- *
- * NOTE: Even though the FLAC file is not open while the chain is being
- *       manipulated, you must not alter the file externally during
- *       this time.  The chain assumes the FLAC file will not change
- *       between the time of FLAC__metadata_chain_read() and
- *       FLAC__metadata_chain_write().
- *
- * NOTE: Do not modify the is_last, length, or type fields of returned
- *       FLAC__MetaDataType objects.  These are managed automatically.
- */
+       if(0 == (iterator = FLAC__metadata_iterator_new()))
+               return die_("allocating memory for iterator");
 
-/*
- * opaque structure definitions
- */
-struct FLAC__MetaData_Chain;
-typedef struct FLAC__MetaData_Chain FLAC__MetaData_Chain;
-struct FLAC__MetaData_Iterator;
-typedef struct FLAC__MetaData_Iterator FLAC__MetaData_Iterator;
-
-typedef enum {
-       FLAC__METADATA_CHAIN_STATUS_OK = 0,
-       FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT,
-       FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE,
-       FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE,
-       FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE,
-       FLAC__METADATA_CHAIN_STATUS_READ_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR,
-       FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR
-} FLAC__MetaData_Chain_Status;
-
-/*********** FLAC__MetaData_Chain ***********/
-
-/*
- * Constructor/destructor
- */
-FLAC__MetaData_Chain *FLAC__metadata_chain_new();
-void FLAC__metadata_chain_delete(FLAC__MetaData_Chain *chain);
+       our_current_position = 0;
 
-/*
- * Get the current status of the chain.  Call this after a function
- * returns false to get the reason for the error.  Also resets the status
- * to FLAC__METADATA_CHAIN_STATUS_OK
- */
-FLAC__MetaData_Chain_Status FLAC__metadata_chain_status(FLAC__MetaData_Chain *chain);
+       FLAC__metadata_iterator_init(iterator, chain);
 
-/*
- * Read all metadata into the chain
- */
-FLAC__bool FLAC__metadata_chain_read(FLAC__MetaData_Chain *chain, const char *filename);
+       if(0 == (block = FLAC__metadata_iterator_get_block(iterator)))
+               return die_("getting block from iterator");
 
-/*
- * Write all metadata out to the FLAC file.  This function tries to be as
- * efficient as possible; how the metadata is actually written is shown by
- * the following:
- *
- * If the current chain is the same size as the existing metadata, the new
- * data is written in place.
- *
- * If the current chain is longer than the existing metadata, the entire
- * FLAC file must be rewritten.
- *
- * If the current chain is shorter than the existing metadata, and
- * use_padding is true, a PADDING block is added to the end of the new
- * data to make it the same size as the existing data (if possible, see
- * the note to FLAC__metadata_simple_iterator_set_block() about the four
- * byte limit) and the new data is written in place.  If use_padding is
- * false, the entire FLAC file is rewritten.
- *
- * If 'preserve_file_stats' is true, the owner and modification time will
- * be preserved even if the FLAC file is written.
- */
-FLAC__bool FLAC__metadata_chain_write(FLAC__MetaData_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats);
+       FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_STREAMINFO);
 
-/*
- * This function will merge adjacent PADDING blocks into a single block.
- *
- * NOTE: this function does not write to the FLAC file, it only
- * modifies the chain.
- *
- * NOTE: Any iterator on the current chain will become invalid after this
- * call.  You should delete the iterator and get a new one.
- */
-void FLAC__metadata_chain_merge_padding(FLAC__MetaData_Chain *chain);
+       block->data.stream_info.sample_rate = 32000;
+       if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true))
+               return die_("copying object");
 
-/*
- * This function will move all PADDING blocks to the end on the metadata,
- * then merge them into a single block.
- *
- * NOTE: this function does not write to the FLAC file, it only
- * modifies the chain.
- *
- * NOTE: Any iterator on the current chain will become invalid after this
- * call.  You should delete the iterator and get a new one.
- */
-void FLAC__metadata_chain_sort_padding(FLAC__MetaData_Chain *chain);
+       if(!compare_chain_(chain))
+               return false;
 
+       if(!FLAC__metadata_chain_write(chain, /*use_padding=*/false, /*preserve_file_stats=*/true))
+               return die_c_("during FLAC__metadata_chain_write(chain, false, true)", FLAC__metadata_chain_status(chain));
 
-/*********** FLAC__MetaData_Iterator ***********/
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
+               return false;
 
-/*
- * Constructor/destructor
- */
-FLAC__MetaData_Iterator *FLAC__metadata_iterator_new();
-void FLAC__metadata_iterator_delete(FLAC__MetaData_Iterator *iterator);
+       printf("[S]P\tnext\n");
+       if(!FLAC__metadata_iterator_next(iterator))
+               return die_("iterator ended early\n");
+       our_current_position++;
 
-/*
- * Initialize the iterator to point to the first metadata block in the
- * given chain.
- */
-void FLAC__metadata_iterator_init(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Chain *chain);
+       printf("S[P]\treplace PADDING with identical-size APPLICATION\n");
 
-/*
- * These move the iterator forwards or backwards, returning false if
- * already at the end.
- */
-FLAC__bool FLAC__metadata_iterator_next(FLAC__MetaData_Iterator *iterator);
-FLAC__bool FLAC__metadata_iterator_prev(FLAC__MetaData_Iterator *iterator);
+       if(0 == (app = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)))
+               return die_("FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)");
+       memcpy(app->data.application.id, "duh", (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8));
+       if(!FLAC__metadata_object_application_set_data(app, data, block->length-(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), true))
+               return die_("setting APPLICATION data");
+       if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/false))
+               return die_("copying object");
+       if(!FLAC__metadata_iterator_set_block(iterator, app))
+               return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain));
 
-/*
- * Get the type of the metadata block at the current position.
- */
-FLAC__MetaDataType FLAC__metadata_iterator_get_block_type(const FLAC__MetaData_Iterator *iterator);
+       if(!compare_chain_(chain))
+               return false;
 
-/*
- * Get the metadata block at the current position.  You can modify 
- * the block in place but must write the chain before the changes
- * are reflected to the FLAC file.
- *
- * Do not call FLAC__metadata_object_delete() on the returned object;
- * to delete a block use FLAC__metadata_iterator_delete_block().
- */
-FLAC__StreamMetaData *FLAC__metadata_iterator_get_block(FLAC__MetaData_Iterator *iterator);
-
-/*
- * Removes the current block from the chain.  If replace_with_padding is
- * true, the block will instead be replaced with a padding block of equal
- * size.  You can not delete the STREAMINFO block.  The iterator will be
- * left pointing to the block before the one just 'deleted', even if
- * 'replace_with_padding' is true.
- */
-FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__MetaData_Iterator *iterator, FLAC__bool replace_with_padding);
-
-/*
- * Insert a new block before or after the current block.  You cannot
- * insert a block before the first STREAMINFO block.  You cannot
- * insert a STREAMINFO block as there can be only one, the one that
- * already exists at the head when you read in a chain.  The iterator
- * will be left pointing to the new block.
- */
-FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block);
-FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block);
+       if(!FLAC__metadata_chain_write(chain, /*use_padding=*/false, /*preserve_file_stats=*/false))
+               return die_c_("during FLAC__metadata_chain_write(chain, false, true)", FLAC__metadata_chain_status(chain));
 
+       if(!test_file_(flacfile_, decoder_metadata_callback_compare_))
+               return false;
 
-/******************************************************************************
-       The following are methods for manipulating the defined block types.
-       Since many are variable length we have to be careful about the memory
-       management.  We decree that all pointers to data in the object are
-       owned by the object and memory-managed by the object.
-
-       Use the _new and _delete functions to create all instances.  When
-       using the _set_ functions to set pointers to data, set 'copy' to true
-       to have the function make it's own copy of the data, or to false to
-       give the object ownership of your data.  In the latter case your pointer
-       must be freeable by free() and will be free()d when the object is
-       _delete()d.
-
-       The _new and _copy function will return NULL in the case of a memory
-       allocation error, otherwise a new object.  The _set_ functions return
-       false in the case of a memory allocation error.
-
-       We don't have the convenience of C++ here, so note that the library
-       relies on you to keep the types straight.  In other words, if you pass,
-       for example, a FLAC__StreamMetaData* that represents a STREAMINFO block
-       to FLAC__metadata_object_application_set_data(), you will get an
-       assertion failure.
-
-       There is no need to recalculate the length field on metadata blocks
-       you have modified.  They will be calculated automatically before they
-       are written back to a file.
-******************************************************************************/
+       FLAC__metadata_iterator_delete(iterator);
 
+       printf("delete chain\n");
 
-/******************************************************************
- * Common to all the types derived from FLAC__StreamMetaData:
- */
-FLAC__StreamMetaData *FLAC__metadata_object_new(FLAC__MetaDataType type);
-FLAC__StreamMetaData *FLAC__metadata_object_copy(const FLAC__StreamMetaData *object);
-void FLAC__metadata_object_delete(FLAC__StreamMetaData *object);
-
-/******************************************************************
- * FLAC__StreamMetaData_Application
- * ----------------------------------------------------------------
- * Note: 'length' is in bytes.
- */
-FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetaData *object, FLAC__byte *data, unsigned length, FLAC__bool copy);
-
-/******************************************************************
- * FLAC__StreamMetaData_SeekPoint
- * ----------------------------------------------------------------
- * Note that we do not manipulate individual seek points as the
- * seek table holds a pointer to an array of seek points.  You can
- * use the _resize function to alter in.  If the size shrinks,
- * elements will truncated; if it grows, new elements will be added
- * to the end.
- */
-FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_new(unsigned num_points);
-FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_copy(const FLAC__StreamMetaData_SeekPoint *object_array, unsigned num_points);
-void FLAC__metadata_object_seekpoint_array_delete(FLAC__StreamMetaData_SeekPoint *object_array);
-FLAC__bool FLAC__metadata_object_seekpoint_array_resize(FLAC__StreamMetaData_SeekPoint **object_array, unsigned old_num_points, unsigned new_num_points);
+       FLAC__metadata_chain_delete(chain);
 
-/******************************************************************
- * FLAC__StreamMetaData_SeekTable
- */
-FLAC__bool FLAC__metadata_object_seektable_set_points(FLAC__StreamMetaData *object, FLAC__StreamMetaData_SeekPoint *points, unsigned num_points, FLAC__bool copy);
+       if(!remove_file_(flacfile_))
+               return false;
 
-/******************************************************************
- * FLAC__StreamMetaData_VorbisComment_Entry
- * ----------------------------------------------------------------
- * This is similar to FLAC__StreamMetaData_SeekPoint.
- */
-FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_new(unsigned num_comments);
-FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_copy(const FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments);
-void FLAC__metadata_object_vorbiscomment_entry_array_delete(FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments);
-FLAC__bool FLAC__metadata_object_vorbiscomment_entry_array_resize(FLAC__StreamMetaData_VorbisComment_Entry **object_array, unsigned old_num_comments, unsigned new_num_comments);
+       return true;
+}
 
-/******************************************************************
- * FLAC__StreamMetaData_VorbisComment
- */
-FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetaData *object, FLAC__byte *entry, unsigned length, FLAC__bool copy);
-FLAC__bool FLAC__metadata_object_vorbiscomment_set_comments(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *comments, unsigned num_comments, FLAC__bool copy);
+int test_metadata_file_manipulation(const char *progname)
+{
+       printf("\n+++ unit test: metadata manipulation\n\n");
 
-#endif
+       our_metadata_.num_blocks = 0;
+
+       if(!test_level_0_(progname))
+               return 1;
+
+       if(!test_level_1_(progname))
+               return 1;
+
+       if(!test_level_2_(progname))
+               return 1;
+
+       return 0;
+}