package tests::ConfigSpecTest;
use strict;
use base qw/Test::Unit::TestSuite/;
sub name {
return "Configuration Specification Tests";
}
sub include_tests {
return qw/tests::ConfigListSpecTest tests::ConfigTypeSpecTest
tests::ConfigFileSpecTest tests::ConfigExecutableSpecTest
tests::ConfigDirectorySpecTest tests::ConfigBooleanSpecTest
tests::ConfigStringSpecTest tests::ConfigIntegerSpecTest
tests::ConfigSelectSpecTest tests::ConfigOptionSpecTest
tests::ConfigConfigSpecTest tests::ConfigCommandSpecTest
/;
}
package tests::ConfigTypeSpecBase;
use base qw/Lire::Test::TestCase tests::TestStoreFixture /;
use Lire::Config::TypeSpec;
sub new {
my $self = shift()->SUPER::new( @_ );
$self->init();
$self->init_i18n();
return $self;
}
sub set_up {
my $self = $_[0];
$self->SUPER::set_up();
$self->set_up_locale();
$self->set_up_spec();
return;
}
sub tear_down {
my $self = $_[0];
$self->SUPER::tear_down();
$self->tear_down_locale();
return;
}
sub set_up_spec {
my $self = $_[0];
$self->{'spec'} = $self->type()->new( 'name' => "test_spec",
$self->additional_new_params() );
}
sub additional_new_params { return () }
sub test_new {
my $self = $_[0];
my $type = $self->type();
my $spec = $type->new( 'name' => "parameter",
'summary' => "Parameter Summary",
'description' => "Parameter Description",
'required' => 0,
'obsolete' => 1,
$self->additional_new_params(),
);
$self->assert_isa( $type, $spec );
$self->assert_str_equals( "parameter", $spec->name() );
$self->assert_str_equals( "Parameter Summary", $spec->summary() );
$self->assert_str_equals( "Parameter Description",
$spec->description() );
$self->assert( ! $spec->required(), '!required()' );
$self->assert( $spec->obsolete(), 'obsolete()' );
$self->assert_dies( qr/missing 'name' parameter/,
sub { $type->new( $self->additional_new_params() ) } );
$self->assert_dies( qr/name should only contain/,
sub { $type->new( 'name' => 'wrong%name',
$self->additional_new_params() ) } );
}
package MockTypeSpec;
use base qw/Lire::Config::TypeSpec/;
sub instance_class {
return "MockValue";
}
package MockValue;
use base qw/Lire::Config::Value/;
package tests::ConfigTypeSpecTest;
use base qw/tests::ConfigTypeSpecBase/;
use Lire::Utils qw/deep_copy/;
sub type {
return "MockTypeSpec";
}
sub test_instance {
my $self = $_[0];
my $value = $self->{'spec'}->instance();
$self->assert_isa( 'MockValue', $value );
$value->{'my_attribute'} = 3;
$self->{'spec'}{'_default'} = $value;
my $default = $self->{'spec'}->instance();
$self->assert_str_not_equals( $value, $default );
$self->assert_deep_equals( $value, $default );
}
sub test_default {
my $self = $_[0];
my $spec = $self->{'spec'};
$self->assert( ! $spec->has_default(), "!has_default()" );
$self->assert_null( $spec->default(), "default() -> !undef" );
my $value = $spec->instance();
$spec->default( $value );
$self->assert_str_equals( $value, $spec->{'_default'} );
$self->assert( $spec->has_default(), "has_default() != 1" );
$self->assert_str_equals( $value, $spec->default() );
my $spec2 = deep_copy( $spec );
$self->assert_dies( qr/default value doesn't come from this specification/,
sub { $spec->default( $spec2->instance() ) } );
$spec->default( undef );
$self->assert_null( $spec->{'_default'} );
}
sub test_required {
my $self = $_[0];
my $spec = $self->{'spec'};
$self->assert_num_equals( 1, $spec->{'_required'} );
$self->assert_num_equals( 1, $spec->required() );
$spec->required( 0 );
$self->assert_num_equals( 0, $spec->{'_required'} );
}
sub test_obsolete {
my $self = $_[0];
my $spec = $self->{'spec'};
$self->assert_num_equals( 0, $spec->{'_obsolete'} );
$self->assert_num_equals( 0, $spec->obsolete() );
$spec->obsolete( 1 );
$self->assert_num_equals( 1, $spec->{'_obsolete'} );
}
sub test_is_instance {
my $self = $_[0];
my $value = $self->{'spec'}->instance();
$self->assert( $self->{'spec'}->is_instance( $value ),
"is_instance() returned false",
);
$self->assert_dies( qr/missing \'instance\' parameter/,
sub { $self->{'spec'}->is_instance() } );
$self->assert_dies( qr/\'instance\' parameter should be a \'Lire::Config::Value\' instance, not/,
sub { $self->{'spec'}->is_instance( $self->{'spec'}) } );
my $bad_value = $self->type()->new( 'name'=> "bad_name",
$self->additional_new_params(),
)->instance();
$self->assert( ! $self->{'spec'}->is_instance( $bad_value ),
"is_instance() with value with different name should return false"
);
my $clone = $self->type()->new( 'name' => $self->{'spec'}->name(),
$self->additional_new_params(),
)->instance();
$self->assert( ! $self->{'spec'}->is_instance( $clone ),
"is_instance() with different spec should fail" );
}
sub test_summary {
my $self = $_[0];
$self->assert_str_equals( "test_spec", $self->{'spec'}->summary() );
$self->{'spec'}->summary( 'Test Summary' );
$self->assert_str_equals( "Test Summary", $self->{'spec'}{'summary'} );
$self->assert_str_equals("Test Summary", $self->{'spec'}->summary() );
$self->{'spec'}->summary( "Summary with\nnewlines \n and wawas" );
$self->assert_str_equals( "Summary with\nnewlines \n and wawas",
$self->{'spec'}{'summary'} );
$self->assert_str_equals( 'Summary with newlines and wawas',
$self->{'spec'}->summary() );
no warnings 'redefine';
my $dgettext_param = '';
my $dgettext = sub {
$dgettext_param = $_[1];
return " dgettext\ncalled\n";
};
local *Lire::Config::TypeSpec::dgettext = $dgettext;
$self->assert_str_equals( " dgettext\ncalled\n",
$self->{'spec'}->summary() );
$self->assert_str_equals ( 'Summary with newlines and wawas',
$dgettext_param );
return;
}
sub test_description {
my $self = $_[0];
$self->assert_null( $self->{'spec'}->description(),
"uninitialized description should be empty" );
my $desc = "Description";
$self->{'spec'}->description( $desc );
$self->assert_equals( $desc, $self->{'spec'}->description() );
$self->assert_dies( qr/'description' parameter should be a DocBook/,
sub {$self->{'spec'}->description( "Description" ) } );
$desc = "Multi-line\n description";
$self->{'spec'}->description( $desc );
$self->assert_equals( "Multi-line description",
$self->{'spec'}->description() );
}
sub test_summary_i18n {
my $self = $_[0];
my $spec = $self->{'spec'};
$spec->{'i18n_domain'} = 'lire-test';
$spec->{'summary'} = 'JPEG Files';
$self->annotate( "There is a bug somewhere that seems to prevent the locale to be set correctly to 'C'." );
$self->assert_str_equals( 'JPEG Files', $spec->summary() );
$self->set_locale( 'fr_CA.iso8859-1' );
$self->assert_str_equals( 'Fichiers JPEG', $spec->summary() );
return;
}
sub test_description_i18n {
my $self = $_[0];
my $spec = $self->{'spec'};
$spec->{'description'} = 'JPEG Files';
$spec->{'i18n_domain'} = 'lire-test';
$self->annotate( "There is a bug somewhere that seems to prevent the locale to be set correctly to 'C'." );
$self->assert_str_equals( 'JPEG Files',
$spec->description() );
$self->set_locale( 'fr_CA.iso8859-1' );
$self->assert_str_equals( 'Fichiers JPEG',
$spec->description() );
}
sub test_text_description {
my $self = $_[0];
my $spec = $self->{'spec'};
$spec->{'description'} = "A parameter's description";
$self->assert_str_equals( "A parameter's description",
$spec->text_description() );
$spec->{'description'} = <A very very long multi-paragraphs text description for
this parameter. It is really long for a filled paragraph.
This is yet another paragraph. This description never
ends.
EOT
my $expected = "A very very long multi-paragraphs text description for this parameter. It is really long for a filled paragraph.\n\nThis is yet another paragraph. This description never ends.";
$self->assert_str_equals( $expected, $spec->text_description() );
}
package tests::ConfigScalarSpecTest;
use base qw/tests::ConfigTypeSpecBase/;
use Lire::Config::ScalarSpec;
sub test_normalize {
my $self = $_[0];
$self->assert_null( $self->{'spec'}->normalize( undef ),
"normalize( undef ) should return undef",
);
my %normalize_test_data = $self->normalize_test_data;
while ( my ( $value, $expected ) = each %normalize_test_data ) {
my $normalized = $self->{'spec'}->normalize( $value );
if ( ! defined $expected ) {
$self->assert_null( $normalized,
'expected undef from ' . ref( $self->{'spec'}) . "->normalize( $value ), got " . ($normalized || '') );
} else {
$self->assert_str_equals( $expected, $normalized );
}
}
}
sub test_is_valid {
my $self = $_[0];
my $warning = '';
local $SIG{'__WARN__'} = sub { $warning .= join "", @_ };
$self->assert( ! $self->{'spec'}->is_valid( undef ),
"is_valid( undef ) returned true" );
$self->assert( ! $warning, "is_valid( undef ) had warnings: $warning" );
foreach my $v ( $self->valid_test_data ) {
$self->assert( $self->{'spec'}->is_valid( $v ),
$self->type(). "->is_valid should return true for $v" );
$self->assert( ! $warning, "is_valid( '$v' ) had warnings: $warning" );
}
foreach my $v ( $self->invalid_test_data ) {
$self->assert( !$self->{'spec'}->is_valid( $v ),
$self->type(). "->is_valid should return false for $v" );
$self->assert( ! $warning, "is_valid( '$v' ) had warnings: $warning" );
}
$self->{'spec'}->required( 0 );
$self->assert_num_equals( 1, $self->{'spec'}->is_valid( undef ) );
}
package tests::ConfigFileSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::FileSpec;
sub type {
return 'Lire::Config::FileSpec';
}
sub normalize_test_data {
return (
"/etc/passwd" => "/etc/passwd",
"~/tmp" => "$ENV{'HOME'}/tmp",
);
}
sub valid_test_data {
return ( "/etc/passwd", __FILE__ );
}
sub invalid_test_data {
return ( "~", "/no/such/file", "/dev/null" );
}
package tests::ConfigExecutableSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::ExecutableSpec;
sub set_up {
my $self = $_[0];
$self->SUPER::set_up();
$self->{'old_PATH'} = $ENV{'PATH'};
$ENV{'PATH'} = '/bin:/sbin';
}
sub tear_down {
my $self = $_[0];
$self->SUPER::tear_down();
$ENV{'PATH'} = defined $self->{'old_PATH'} ? $self->{'old_PATH'} : '';
}
sub type {
return 'Lire::Config::ExecutableSpec';
}
sub normalize_test_data {
return (
"sh" => "/bin/sh",
"/bin/sh" => "/bin/sh",
"echo" => "/bin/echo",
"bin/echo" => "bin/echo",
"/./bin/echo" => "/bin/echo",
);
}
sub valid_test_data {
return ( "sh", "/bin/sh", "/bin//cat" );
}
sub invalid_test_data {
return ( "/bin/nosuchfile", "/etc", "/dev/null", "/etc/passwd" );
}
package tests::ConfigDirectorySpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::DirectorySpec;
use File::Basename;
sub type {
return 'Lire::Config::DirectorySpec';
}
sub normalize_test_data {
return (
"/etc/passwd" => "/etc/passwd",
"/bin/./../" => "/bin/..",
"~" => "$ENV{'HOME'}",
);
}
sub valid_test_data {
return ( ".", "/bin", dirname( __FILE__ ) . "/../../" );
}
sub invalid_test_data {
return ( "/bin/nosuchfile", __FILE__, "/dev/null", "/etc/passwd" );
}
package tests::ConfigListSpecTest;
use base qw/tests::ConfigCompoundSpecTest/;
use Lire::Config::ListSpec;
sub type {
return 'Lire::Config::ListSpec';
}
package tests::ConfigConfigSpecTest;
use base qw/tests::ConfigTypeSpecBase/;
use Lire::Config::ConfigSpec;
sub test_components_by_section {
my $self = $_[0];
my $spec = $self->{'spec'};
my $test1_spec = new Lire::Config::StringSpec( 'name' => 'test1',
'section' => 'section1' );
my $test2_spec = new Lire::Config::StringSpec( 'name' => 'test2',
'section' => 'section2' );
my $test3_spec = new Lire::Config::StringSpec( 'name' => 'test3',
'section' => 'section1' );
$spec->add( $test1_spec );
$spec->add( $test2_spec );
$spec->add( $test3_spec );
$self->assert_dies( qr/missing 'section' parameter/,
sub { $spec->components_by_section() } );
$self->assert_deep_equals( [ $test1_spec, $test3_spec ],
$spec->components_by_section( 'section1' ) );
$self->assert_deep_equals( [ $test2_spec ],
$spec->components_by_section( 'section2' ) );
$self->assert_deep_equals( [],
$spec->components_by_section( 'section3' ) );
}
sub type {
return 'Lire::Config::ConfigSpec';
}
sub test_new {
my $self = $_[0];
my $spec = $self->type()->new();
$self->assert_isa( $self->type(), $spec );
}
package tests::ConfigStringSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::StringSpec;
sub test_valid_re {
my $self = $_[0];
my $string = new Lire::Config::StringSpec( 'name' => 'valid-re',
'valid-re' => '^\w+$' );
$self->assert_str_equals( qr/^\w+$/, $string->{'_compiled_re'} );
$self->assert_str_equals( '^\w+$', $string->valid_re() );
$string->valid_re( undef );
$self->assert_null( $string->{'_compiled_re'} );
$self->assert_null( $string->{'_valid_re'} );
$string->valid_re( '^\d+$' );
$self->assert( $string->is_valid( 10 ), 'is_valid( 10 )' );
$self->assert( ! $string->is_valid( 'abc' ), '!is_valid( "abc" )' );
}
sub type {
return 'Lire::Config::StringSpec';
}
sub normalize_test_data {
return ( 'test' => "test",
"another string" => "another string",
);
}
sub valid_test_data {
return ( "test", "string", 10 );
}
sub invalid_test_data {
return ();
}
package tests::ConfigIntegerSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::IntegerSpec;
sub type {
return 'Lire::Config::IntegerSpec';
}
sub normalize_test_data {
return (
0 => 0,
-1_100_200 => -1100200,
1 => 1,
);
}
sub valid_test_data {
return ( 0, 1, -1, 1_200_300, -1_100, "-10", "-1_000_000" );
}
sub invalid_test_data {
return ( 0.1, "two" );
}
package tests::ConfigBooleanSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::BooleanSpec;
sub type {
return 'Lire::Config::BooleanSpec';
}
sub normalize_test_data {
return (
'' => '',
'0' => '',
'1' => 1,
'enabled' => 1,
'disabled' => '',
'on' => 1,
'off' => '',
'Yes' => 1,
'yes' => 1,
'no' => '',
'NO' => '',
'F' => '',
'FALSE' => '',
'TRUE' => 1,
't' => 1,
);
}
sub valid_test_data {
return ('', qw/0 1 enabled DISABLED Off oN Yes no True false t f/);
}
sub invalid_test_data {
return qw/y n boolean/;
}
package tests::ConfigSelectSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::SelectSpec;
sub set_up {
my $self = $_[0];
$self->SUPER::set_up;
$self->{'spec'}->add( new Lire::Config::OptionSpec( 'name' => "option_1" ) );
$self->{'spec'}->add( new Lire::Config::OptionSpec( 'name' => "option_2" ) );
$self->{'spec'}->add( new Lire::Config::OptionSpec( 'name' => "option_3" ) );
}
sub type {
return "Lire::Config::SelectSpec";
}
sub normalize_test_data {
return (
'option_1' => "option_1",
'option_2' => "option_2",
'option_3' => "option_3",
'option_4' => undef,
'OPTION_1' => "option_1",
'OpTiOn_2' => "option_2",
);
}
sub valid_test_data {
return qw/option_1 Option_2 OPTION_3 option_3 option_2/;
}
sub invalid_test_data {
return qw/option_4/;
}
sub test_add {
my $self = $_[0];
my $select = new Lire::Config::SelectSpec( 'name' => "select" );
$self->assert_equals( 0, scalar $select->options );
my $option_1 = new Lire::Config::OptionSpec( 'name' => "option_1" );
$select->add( $option_1 );
$self->assert_deep_equals( [ $option_1 ], [ $select->options ] );
my $option_2 = new Lire::Config::OptionSpec( 'name' => "option_2" );
$select->add( $option_2 );
$self->assert_deep_equals( [ sort ($option_1, $option_2) ],
[ sort $select->options ] );
$self->assert_dies( qr/missing 'option' parameter/,
sub { $select->add( undef ) } );
# $self->assert_dies( qr/already contains an option named 'option_1'/,
# sub { $select->add( new Lire::Config::OptionSpec( 'name' => "OPTION_1" ) ) },
# );
$self->assert_dies( qr/\'option\' parameter should be a \'Lire::Config::OptionSpec\' instance, not \'Lire::Config::ListSpec/,
sub { $select->add( new Lire::Config::ListSpec( 'name' => "OPTION_1" ) ) } );
}
package tests::ConfigOptionSpecTest;
use base qw/tests::ConfigTypeSpecBase/;
use Lire::Config::OptionSpec;
sub type {
return "Lire::Config::OptionSpec";
}
sub test_instance {
my $self = $_[0];
$self->assert_dies( qr/not implemented/,
sub { $self->{'spec'}->instance() });
}
sub test_is_instance {
my $self = $_[0];
my $scalar = new Lire::Config::Scalar( 'spec' => new Lire::Config::ScalarSpec( name => "test" ) );
$self->assert( !$self->{'spec'}->is_instance( $scalar ),
"is_instance() should return false" );
}
package tests::ConfigCommandSpecTest;
use base qw/tests::ConfigScalarSpecTest/;
use Lire::Config::CommandSpec;
use File::Basename;
sub set_up {
my $self = $_[0];
$self->SUPER::set_up();
$self->{'old_PATH'} = $ENV{'PATH'};
$ENV{'PATH'} = '/bin:/sbin';
}
sub tear_down {
my $self = $_[0];
$self->SUPER::tear_down();
$ENV{'PATH'} = defined $self->{'old_PATH'} ? $self->{'old_PATH'} : '';
}
sub type {
return 'Lire::Config::CommandSpec';
}
sub normalize_test_data {
return (
"//bin//sh" => "/bin/sh",
"sh" => "/bin/sh",
);
}
sub valid_test_data {
return ( "sh", "/bin/sh", "ls", "cat" );
}
sub invalid_test_data {
return ( "/bin/nosuchfile", __FILE__, "/dev/null" );
}
1;