package tests::DlfConverterProcessFixture;

use strict;

use base qw/ Lire::Test::TestCase /;

use Cwd qw/ realpath /;
use File::Basename qw/ dirname /;
use File::Temp qw/ :mktemp tempdir /;

use Lire::PluginManager;
use Lire::DlfConverterProcess;
use Lire::DlfStore;
use Lire::ImportJob;
use Lire::DlfSchema;
use Lire::Utils qw/ check_param /;
use Lire::Test::Mock;

=pod

=head1 NAME

Lire::Test::DlfConverterTestCase - Base class for writing DLfConverter unit tests

=head1 SYNOPSIS

  use base qw/ Lire::Test::DlfConverterTestCase /;

  sub schema_fixtures {
      {
        'test' => "...",
        'test2' => "...",
      },
  }

  sub import_job_fixtures {
      {
          log
      },
  }

=head1 DESCRIPTION

This is a Test::Unit::TestCase subclass which can be used to easily
write tests for DlfConverters. This class implements what is needed to
provide the necessary fixture in the set_up() and tear_down().

=head1 FIXTURES

These are the methods that subclasses will usually override to provide
the necessary data so that the DlfConverter test fixtures be setup.

=head2 schema_fixtures()

Should return an hash reference. Keys are schema names and the value
should be the schema in XML. The class will make sure that these
schemas can be loaded using load_schema().

=cut

sub schema_fixtures {
    return {};
}

=pod

=head2 import_job_fixtures()

Should return an hash reference. Keys are the ImportJob name and the
value should the log file's content. Appropriate Lire::ImportJob can
then be acessed using the import_job() method.

=cut

sub import_job_fixtures {
    return {};
}

=pod

=head2 converter_fixtures()

Should return an array reference containing the Lire::DlfConverter
object which should be registered with the Lire::PluginManager.

=cut

sub converter_fixtures {
    return [];
}

sub new {
    my $self = shift()->SUPER::new( @_ );

    my $tmpl = ref($self) . "_XXXXXX";
    $tmpl =~ s/::/__/;
    $self->{'_tmpdir'} = tempdir( $tmpl,
                                  'TMPDIR' => 1,
                                  'CLEANUP' => 1
                                );

    my $schemas = $self->schema_fixtures();
    $self->error( "schema_fixtures() didn't return an hash reference: $schemas" )
      unless ref $schemas eq 'HASH';

    foreach my $s ( keys %$schemas ) {
        open FH, "> $self->{'_tmpdir'}/$s.xml"
          or $self->error( "error creating $s schema: $!" );
        print FH $schemas->{$s};
        close FH;
    }

    my $logs = $self->import_job_fixtures();
    $self->error( "import_job_fixtures() didn't return an hash reference: $logs" )
      unless ref $logs eq 'HASH';
    foreach my $l ( keys %$logs ) {
        open FH, "> $self->{'_tmpdir'}/$l.log"
          or $self->error( "can' create log file $l: $!" );
        print FH $logs->{$l};
        close FH;

        $self->{'_jobs'}{$l} = new Lire::ImportJob( $l );
        $self->{'_jobs'}{$l}->pattern( "$self->{'_tmpdir'}/$l.log" );
    }

    $self->{'_test_count'} = 0;

    return $self;
}

=pod

=head1 UTILITY METHODS

=head2 dlf_store()

Return the Lire::DlfStore instance that should be used for the test. A
new Lire::DlfStore is created before each test.

=cut

sub dlf_store {
    return $_[0]{'_store'};
}


=pod

=head2 import_job( $name )

Returns the Lire::ImportJob named $name which points to the corresponding
log file content as returned by import_job_fixtures().

=cut

sub import_job {
    check_param( $_[1], 'name', qr/^.+$/ );
    return $_[0]{'_jobs'}{$_[1]};
}

sub set_up {
    my $self = $_[0];
    $self->SUPER::set_up();

    $self->{'old_cache'} = { %Lire::DlfSchema::SCHEMA_CACHE };
    %Lire::DlfSchema::SCHEMA_CACHE = ();

    $self->{'cfg'}{'_lr_config_spec'} = $self->lire_default_config_spec();
    $self->{'_store'} = Lire::DlfStore->open( "$self->{'_tmpdir'}/store" .
                                            $self->{'_test_count'}++, 1 );

    my $lire_schemas_dir =
      realpath( dirname( __FILE__ ) . '/../../schemas' );
    $self->{'cfg'}{'lr_schemas_path'} = [ $lire_schemas_dir,
                                          $self->{'_tmpdir'} ];

    $self->{'_converter_fixtures'} = $self->converter_fixtures();
    $self->set_up_plugin_mgr();
    foreach my $converter ( @{$self->{'_converter_fixtures'}} ) {
        Lire::PluginManager->register_plugin( $converter );
    }

    return;
}

sub tear_down {
    my $self = $_[0];
    $self->SUPER::tear_down();

    delete $self->{'_converter_fixtures'};
     %Lire::DlfSchema::SCHEMA_CACHE = %{$self->{'old_cache'}};
    return;
}

=pod

=head2 create_dlf_converter_process( $job_name, $converter_name )

Returns a Lire::DlfConverterProcess to process the ImportJob $job_name
using the DLF converter $converter_name.

=cut

sub create_dlf_converter_process {
    my ( $self, $job_name, $converter_name ) = @_;

    $self->import_job( $job_name )->converter( $converter_name );
    my $p = 
      new_proxy Lire::Test::Mock( 'Lire::DlfConverterProcess',
                                  $self->import_job( $job_name ),
                                  $self->dlf_store() );
    $p->set_result( 'error', sub { $self->annotate( $_[1] );
                                   shift->Lire::DlfConverterProcess::error( @_ ) } );
    $self->assert_not_null( $p, "new() returned undef" );

    return $p;
}

=pod

=head2 assert_store_contains_expected_results( $expected, $process )

This is method can be use to validate the results of a
Lire::DlfConverterProcess. The expected results are specified in the
$expected structure. The $process parameter should contains the
Lire::DlfConverterProcess for which the results should be checked.

The $expected data structure is an hash reference which contains
the expected results. The following keys will be checked:

=over

=item line_count

This is checked against the value returned by line_count() from the
DlfConverterProcess.

=item dlf_count

This value is checked against the value returned by dlf_count() from
the DlfConverterProcess.

=item error_count

This value is checked against the value returned by error_count() from
the DlfConverterProcess.

=item saved_count

This value is checked against the value returned by saved_count() from
the DlfConverterProcess.

=item ignored_count

This value is checked against the value returned by ignored_count()
from the DlfConverterProcess.

=item dlf

This should contains another hash reference. For each keys in this
hash, the DlfStore will be checked for DlfStream of the same name.
These DlfStream will be inspected to make sure that they contains
identical DLF records than the ones specified in the array reference.

=back

=cut

sub assert_dlf_converter_match_results {
    my ( $self, $expected, $p ) = @_;

    my $line_count = $p->line_count(),
    $self->assert_equals( $expected->{'line_count'}, $p->line_count() );
    $self->assert_equals( $expected->{'dlf_count'}, $p->dlf_count() );
    $self->assert_equals( $expected->{'error_count'}, $p->error_count() );
    $self->assert_equals( $expected->{'saved_count'}, $p->saved_count() )
      if defined $expected->{'saved_count'};
    $self->assert_equals( $expected->{'ignored_count'}, $p->ignored_count() )
      if defined $expected->{'ignored_count'};

    my $store = $p->dlf_store();
    foreach my $schema ( keys %{$expected->{'dlf'}} ) {
        my $s = $store->open_dlf_stream( $schema, "r" );
        my @dlf = ();
        while ( defined ($_ = $s->read_dlf() ) ) {
            push @dlf, $_;
        }
        $s->close();
        $self->assert_deep_equals( $expected->{'dlf'}{$schema}, \@dlf );
    }

    return;
}

# keep perl happy
1;

__END__

=pod

=head1 SEE ALSO

Test::Unit::TestCase(3pm), Lire::DlfConverter(3pm),
Lire::DlfConverterProcess(3pm), Lire::DlfStore(3pm),
Lire::ImportJob(3pm), Lire::PluginManager(3pm)

=head1 VERSION

$Id: DlfConverterProcessFixture.pm,v 1.6 2006/07/23 13:16:32 vanbaal Exp $

=head1 AUTHOR

Francis J. Lacoste <flacoste@logreport.org>

=head1 COPYRIGHT

Copyright (C) 2002-2004 Stichting LogReport Foundation LogReport@LogReport.org

This file is part of Lire.

Lire is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html.

=cut



syntax highlighted by Code2HTML, v. 0.9.1