package tests::ConfigTest;

use strict;

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

use Lire::Config;
use Lire::Config::Build qw/ ac_path /;
use Lire::Utils qw/tilde_expand tempdir/;
use File::Path qw/mkpath/;

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

    $self->{'tmpdir'} = tempdir( "config_XXXXXX", 'CLEANUP' => 1 );
    $self->{'confdir'} = "$self->{'tmpdir'}/config";
    mkdir $self->{'confdir'}
      or $self->error( "mkdir $self->{'confdir'} failed: $!" );
    $self->{'specdir'} = "$self->{'tmpdir'}/specs";
    mkdir $self->{'specdir'}
      or $self->error( "mkdir $self->{'specdir'} failed: $!" );
    $self->_make_config_spec;
    $self->_make_main_config_file;
    $self->_make_override_config_file;

    $self;
}

sub _make_config_spec {
    my $self = $_[0];

    my $fh;
    open $fh, "> $self->{'specdir'}/spec.xml"
      or $self->error( "open of $self->{'specdir'}/spec.xml failed: $!" );
    print $fh <<SPEC;
<config-spec xmlns="http://www.logreport.org/LRCSML/">
  <directory name="dir_var"/>
  <directory name="bad_dir_var"/>
  <file name="file_var"/>
  <file name="bad_file_var"/>
  <executable name="exe_var"/>
  <executable name="bad_exe_var"/>

  <boolean name="bool_var"/>
  <list name="list_var">
    <string name="s"/>
  </list>
  <integer name="int_var"/>
  <list name="list_of_dirs">
    <directory name="dir"/>
  </list>
  <select name="output_format">
   <option name="txt"/>
   <option name="html"/>
  </select>
  <select name="wrong_select">
   <option name="option_1"/>
   <option name="option_2"/>
  </select>
  <string name="missing_var"/>
  <string name="cascading_test_var"/>
  <string name="cascading_test_var2"/>
</config-spec>
SPEC
    close $fh;
}

sub _make_main_config_file {
    my $self = $_[0];

    my $fh;
    $self->{'main_config_file'} = "$self->{'confdir'}/main.xml";
    open $fh, "> $self->{'main_config_file'}"
      or $self->error( "open of $self->{'main_config_file'} failed: $!\n" );
    print $fh <<CONFIG;
<config xmlns="http://www.logreport.org/LRCML/">
  <global>
    <param name="dir_var">$self->{'tmpdir'}</param>
    <param name="bool_var">yes</param>
    <param name="bad_dir_var">$self->{'tmpdir'}/nodir</param>
    <param name="file_var">$self->{'main_config_file'}</param>
    <param name="bad_file_var">$self->{'tmpdir'}/no_such_file</param>
    <param name="exe_var">/bin/sh</param>
    <param name="bad_exe_var">$self->{'tmpdir'}/main.xml</param>
    <param name="list_var">
      <param name="s">string 1</param>
      <param name="s">string 2</param>
    </param>
    <param name="int_var">512</param>
    <param name="list_of_dirs">
      <param name="dir">$self->{'tmpdir'}/specs</param>
      <param name="dir">$self->{'tmpdir'}/nosuchdir</param>
    </param>
    <param name="output_format">txt</param>
    <param name="wrong_select">option 3</param>
    <param name="cascading_test_var">main/global</param>
    <param name="cascading_test_var2">main/global</param>
  </global>
</config>
CONFIG
    close $fh;
}

sub _make_override_config_file {
    my $self = $_[0];

    my $fh;
    # must not be placed in $self->{'confdir'}
    $self->{'override_config_file'} = "$self->{'tmpdir'}/override.xml";
    open $fh, "> $self->{'override_config_file'}"
      or $self->error( "open of $self->{'override_config_file'} failed: $!\n");
print $fh <<OVERRIDE;
<config xmlns="http://www.logreport.org/LRCML/">
  <global>
    <param name="int_var">1024</param>
    <param name="wrong_select">option_2</param>
    <param name="cascading_test_var">override/global</param>
    <param name="cascading_test_var2">override/global</param>
  </global>
</config>
OVERRIDE
    close $fh;
}

sub set_up {
    my $self = $_[0];

    # Since Lire::Test::TestCase::set_up messes with Lire::Config,
    # we don't chain up.

    return;
}

sub tear_down {
    my $self = $_[0];

    return;
}

sub test_instance {
    my $self = $_[0];

    my $cfg = Lire::Config->instance;
    $self->assert_not_null( $cfg, "instance() returned undef" );
    $self->assert( $cfg->isa( 'Lire::Config'), "is not a Lire::Config object");
}

sub test_old_config_api {
    my $self = $_[0];

    Lire::Config->init_vars();
    foreach my $varname ( qw/VERSION/ )
    {
        no strict 'refs';
        my $var = "Lire::Config::$varname";
        $self->assert_not_null( ${$var}, "$var is undef" );
    }
}

sub test_config_files {
    my $self = $_[0];

    my @files = Lire::Config->config_files();
    my $cfg = Lire::Config->instance();
    $cfg->add_config_file( $self->{'main_config_file'} );

    my @new_files = $cfg->config_files();
    $self->assert( scalar @new_files == @files + 1,
                   "config_files() doesn't contain one more entry" );

    $self->assert_equals( $self->{'main_config_file'}, $new_files[-1] );

    Lire::Config->del_config_file( $self->{'main_config_file'} );
    @new_files = $cfg->config_files();

    $self->assert_deep_equals( \@files, \@new_files );

    $cfg->add_config_path( $self->{'confdir'} );

    @new_files = $cfg->config_files();
    $self->assert( scalar @new_files == 1 + scalar @files,
                   "config_files() doesn't contain one more entry" );

    $self->assert_equals( $self->{'main_config_file'}, $new_files[@new_files-1] );

    Lire::Config->del_config_file( $self->{'main_config_file'} );
    @new_files = $cfg->config_files;

    $self->assert_deep_equals( \@files, \@new_files );
}

sub test_config_spec_path {
    my ( $self ) = @_;

    my @path = Lire::Config->config_spec_path;
    my $or_dir_count = @path;
    $self->assert_not_null( \@path, "config_spec_path returned undef" );

    my $tmpdir = tempdir( "ConfigTest_XXXXXX", 'CLEANUP' => 1 );
    Lire::Config->add_config_spec_path_dir( $tmpdir );
    my @new_path = Lire::Config->config_spec_path;
    my $new_dir_count = @new_path;
    $self->assert( $new_dir_count == $or_dir_count + 1,
                   "config_spec_path doesn't contain one more entry" );

    $self->assert( grep { $_ eq $tmpdir } @new_path,
                   "config_spec_path doesn't contain new entry" );

    Lire::Config->del_config_spec_path_dir( $tmpdir );
    @new_path = Lire::Config->config_spec_path;

    $self->assert_deep_equals( \@path, \@new_path );
}

sub _set_up_config_files {
    my ( $self, $cfg ) = @_;

    foreach my $f ( $cfg->config_files ) {
        $cfg->del_config_file( $f );
    }
    $cfg->add_config_file( $self->{'main_config_file'} );

    foreach my $d ( $cfg->config_spec_path ) {
        $cfg->del_config_spec_path_dir( $d );
    }
    $cfg->add_config_spec_path_dir( $self->{'specdir'} );
}

sub test_init {
    my $self = $_[0];

    my $cfg = new Lire::Config::XMLFilesConfig();
    $self->_set_up_config_files( $cfg );

    eval { $cfg->get( "dir_var" ) };
    $self->assert_not_null( $@, "get() before init() should throw an exception" );

    my $warnings = 0;
    local $SIG{'__WARN__'} = sub { $warnings++ };

    $cfg->init;
    $self->assert_equals( 0, $warnings );

    $self->assert_equals( $self->{'tmpdir'}, $cfg->get( 'dir_var' ) );
    $self->assert_equals( 1, $cfg->get( 'bool_var' ) );
    $self->assert_null( scalar $cfg->get( "bad_dir_var" ),
                        "invalid directory passed check" );
    $self->assert_equals( $self->{'main_config_file'},
                          $cfg->get( 'file_var' ) );
    $self->assert_null( scalar $cfg->get( "bad_file_var" ),
                        "invalid file passed check" );
    $self->assert_equals( "/bin/sh", $cfg->get( 'exe_var' ) );
    $self->assert_null( scalar $cfg->get( "bad_exe_var" ),
                        "invalid executable passed check" );
    $self->assert_deep_equals( [ "string 1", "string 2" ],
                               $cfg->get( 'list_var' ) );
    $self->assert_equals( 512, $cfg->get( "int_var") );
    $self->assert_deep_equals( [ $self->{'specdir'} ],
                                 $cfg->get( "list_of_dirs" ),
                             );
    $self->assert_equals( "txt", $cfg->get( "output_format" ) );
    $self->assert_null( scalar $cfg->get( "wrong_select" ),
                        "select option with invalid option didn't return undef"
                      );

    $cfg->add_config_file( $self->{'override_config_file'} );
    $self->assert_equals( 1024, $cfg->get( "int_var" ) );
    $self->assert_equals( "option_2", $cfg->get( "wrong_select" ) );

    $cfg->del_config_file( $self->{'override_config_file'} );
    $self->assert_equals( 512, $cfg->get( "int_var" ) );
    $self->assert_null( scalar $cfg->get( "wrong_select" ),
                        "select option with invalid option didn't return undef"
                      );
}

sub test_get_var {
    my $self = $_[0];

    my $cfg = new Lire::Config::XMLFilesConfig();
    $self->_set_up_config_files( $cfg );

    $self->assert_died( sub { $cfg->get_var( 'dir_var' ) },
                        qr/Configuration queried without/ );
    {
        local $SIG{'__WARN__'} = sub {};
        $cfg->init;
    }

    $self->assert_died( sub { $cfg->get_var( 'no_such_var') },
                        qr/No such configuration variable/ );

    my $var = $cfg->get_var( 'dir_var' );
    $self->assert_not_null( $var, "get_var() returned null" );
    $self->assert( UNIVERSAL::isa( $var, 'Lire::Config::Scalar'),
                   "get_var() returned wrong type" );
    $self->assert_equals( $self->{'tmpdir'}, $var->as_value );

    $self->assert_str_equals( $cfg->{'_defaults'}->get( 'missing_var' ),
                              $cfg->get_var( 'missing_var' ) );
}

1;


syntax highlighted by Code2HTML, v. 0.9.1