The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use warnings;
use strict;
use File::Temp qw(tempfile);
use File::Spec;
use Test::More tests => 12 + 2 * 18; # general tests + number of samples * test per sample

BEGIN
{
   use_ok('FLV::Info');
}

my @samples = (
   {
      file => File::Spec->catfile('t', 'samples', 'flash6.flv'),
      expect => {
         video_codec => 'Sorenson H.263',
         duration => '7418',
         audio_format => 'MP3',
         audio_type => 'stereo',
         meta_framerate => '20',

         has_video => 1,
         has_audio => 1,
         tags => 435,
         video_tags => 149,
         audio_tags => 285,
         meta_tags => 1,
      },
   },
   {
      file => File::Spec->catfile('t', 'samples', 'flash8.flv'),
      expect => {
         video_codec => 'On2 VP6',
         duration => '7418',
         audio_format => 'MP3',
         audio_type => 'stereo',
         meta_framerate => '20',

         has_video => 1,
         has_audio => 1,
         tags => 435,
         video_tags => 149,
         audio_tags => 285,
         meta_tags => 1,
      },
   },
);

my @cleanup;

END
{
   # Delete temp files
   unlink $_ for @cleanup;
}

{
   my $reader = FLV::Info->new();
   eval { $reader->parse('nosuchfile.flv'); };
   like($@, qr/Failed to read FLV file/, 'parse non-existent file');

   my ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Unexpected end of file/, 'parse empty file');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'foo';
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Unexpected end of file/, 'parse non-flv file');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'FLV';
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Unexpected end of file/, 'parse non-flv file');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'foo' x 1000;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Not an FLV file/, 'parse long non-flv file');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'FLV'.pack 'CCN', 200, 0, 9;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/only understand FLV version 1/, 'parse badly versioned flv header');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'FLV'.pack 'CCN', 1, 128, 9;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Reserved header flags are non-zero/, 'parse reserved-flag using flv header');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'FLV'.pack 'CCN', 1, 0, 8;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Illegal value for body offset/, 'parse too-small length flv header');

   ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   print {$fh} 'FLV'.pack 'CCNC', 1, 0, 10, 0;
   close $fh;
   eval { $reader->parse($tempfilename); };
   like($@, qr/Unexpected end of file/, 'parse long flv header');

   eval { $reader->get_file()->serialize(); };
   like($@, qr/Please specify a filehandle/, 'serialize with no filehandle');

   # Expect a failure with a warning
   local $SIG{__WARN__} = sub{};
   ok(!$reader->get_file()->serialize($fh), 'serialize with closed filehandle');
}

for my $sample (@samples)
{
   # Read an FLV file and check selected metadata against expectations
   my $reader = FLV::Info->new();
   ok(!scalar $reader->get_info(), 'get_info');
   $reader->parse($sample->{file});
   ok(scalar $reader->get_info(), 'get_info');
   ok($reader->report(), 'report');

   my $flv = $reader->get_file();
   is($flv->get_filename(), $sample->{file}, 'get_filename');

   my %info = (
      $reader->get_info(),
      'has_video'  => $flv->get_header()->has_video(),
      'has_audio'  => $flv->get_header()->has_audio(),
      'tags'       => scalar $flv->get_body()->get_tags(),
      'video_tags' => scalar $flv->get_body()->get_video_frames(),
      'audio_tags' => scalar $flv->get_body()->get_audio_packets(),
      'meta_tags'  => scalar $flv->get_body()->get_meta_tags(),
      #'end_time'   => $flv->get_body()->end_time(),
   );

   #use Data::Dumper;
   #diag Dumper \%info;
   #diag Dumper [$flv->get_body()->get_meta_tags()];
   for my $key (sort keys %{$sample->{expect}})
   {
      is($info{$key}, $sample->{expect}->{$key}, $sample->{file}.' - '.$key);
   }

   # Write the FLV back out as a temp file
   my ($fh, $tempfilename) = tempfile();
   die if (! -f $tempfilename);
   push @cleanup, $tempfilename;
   ok($flv->serialize($fh), 'serialize');
   close $fh;
   
   # Read the temp file back and compare it to the original -- should
   # be identical except for hash key ordering
   my $rereader = FLV::Info->new();
   $rereader->parse($tempfilename);
   my $newflv = $rereader->get_file();

   # remove filename properties which are guaranteed to differ
   $flv->{filename} = undef;
   $newflv->{filename} = undef;
   is_deeply($newflv, $flv, 'compare re-serialized');
   # read it again, this time via filehandle
   open my $fh2, '<', $tempfilename or die;
   binmode $fh2 or die;
   $rereader->parse($fh2);
   close $fh2;
   is_deeply($newflv, $flv, 'compare re-serialized');
}