package tests::ReportSpecTest; use strict; use base qw/ Lire::Test::TestCase tests::TestStoreFixture tests::ChartTypesFixture /; use Lire::ReportSpec; use Lire::DlfSchema; use Lire::FilterExpr; use Lire::Param; use Lire::Group; use Lire::GroupField; use Lire::Aggregate; use Lire::Report; use Lire::Utils qw/tempdir create_file/; use Lire::Config::XMLSpecListSpec; use tests::MockAggregator; use tests::MockAggregate; use Lire::Report::ChartConfig; use Lire::Test::Mock; sub new { my $self = shift->SUPER::new( @_ ); $self->init(); return $self; } sub set_up { my $self = $_[0]; $self->SUPER::set_up(); $self->set_up_test_store(); $self->{'tmp_test_spec_dir'} = $self->{'tmpdir'} . "/test"; mkdir $self->{'tmp_test_spec_dir'} or $self->error( "failed to make $self->{'tmp_test_spec_dir'}: $!\n" ); $self->{'cfg'}{'lr_reports_path'} = [ $self->{'testdir'} . "/reports", $self->{'tmpdir'} ]; $self->set_up_simple_report_spec(); return; } sub tear_down { my $self = $_[0]; $self->SUPER::tear_down( @_ ); return; } sub test_load_report { my $self = $_[0]; my $spec = Lire::ReportSpec->load( "test", "top-files" ); $self->assert_str_equals( "top-files", $spec->id() ); } sub create_test_report_spec { my $self = $_[0]; my $spec = new Lire::ReportSpec(); $spec->id( "test-report" ); $spec->superservice( "test" ); $spec->title( "Test Report Specification" ); $spec->description( "Test Report Specification" ); $spec->display_title( "Test Report" ); $spec->param( "param_int", new Lire::Param( 'name' => "param_int", 'type' => "int", 'i18n_domain' => 'lire-test', ) ); $spec->param( "param_int" )->default( 10 ); $spec->param( "param_match", new Lire::Param( 'name' => "param_match", 'type' => "string", 'i18n_domain' => 'lire-test', ) ); $spec->param( "param_match" )->default( ".*" ); my $cfg = new Lire::Report::ChartConfig(); $cfg->name( 'test-chart' ); $cfg->title( 'Downloads Pie' ); $cfg->get( 'case_var' )->set( 'file' ); $cfg->type( Lire::PluginManager->get_plugin( 'chart_type', 'pie' ) ); $cfg->type_properties()->get( 'values' )->set( 'downloads' ); $spec->add_chart_config( $cfg ); $spec->filter_spec( new Lire::FilterExpr::Match( 'container' => $spec, 'value' => '$file', 're' => '$param_match', ) ); my $group = new Lire::Group( 'report_spec' => $spec, 'limit' => '$param_int', ); $group->group_fields( [ new Lire::GroupField( 'report_spec' => $spec, 'name' => "file", 'i18n_domain' => 'lire-test', ) ] ); $group->ops( [ new Lire::Count( 'report_spec' => $spec, 'parent' => $group, 'name' => "downloads", 'label' => "Downloads", ) ] ); $group->sort_fields( [ "-downloads" ] ); $spec->calc_spec( $group ); return $spec; } sub set_up_simple_report_spec { my $self = $_[0]; $self->{'simple_spec'} = new Lire::ReportSpec(); $self->{'simple_spec'}->superservice( 'test' ); $self->{'simple_spec'}->id( 'test-spec' ); $self->{'simple_spec'}->subreport_id( "test-spec.i" ); $self->{'simple_spec'}->param( 'param1', new Lire::Param( 'name' => 'param1', 'type' => 'int', 'i18n_domain' => 'lire-test', 'default' => 5 ) ); $self->{'simple_spec'}->charttype( 'bars' ); $self->{'simple_spec'}->display_title( 'Top $param1 report' ); $self->{'simple_spec'}->display_description( '$param1' ); my $aggr = new_proxy Lire::Test::Mock( 'tests::MockAggregator', 'report_spec' => $self->{'simple_spec'} ); $aggr->set_result( 'create_entries' => sub { # Create two empty entries $_[1]->create_entry(); $_[1]->create_entry(); } ); $aggr->ops( [ new tests::MockAggregate( 'report_spec' => $self->{'simple_spec'}, 'parent' => $aggr, 'name' => 'aggr' ) ] ); $self->{'simple_spec'}->calc_spec( $aggr ); return; } sub test_print { my $self = $_[0]; $self->set_up_chart_types(); my $spec = $self->create_test_report_spec(); my $specfile = "$self->{'tmp_test_spec_dir'}/test-report.xml"; open my $fh, "> $specfile" or $self->error( "failed to open $specfile for writing: $!\n" ); $spec->print( $fh ); close $fh or $self->error( "close failed: $!\n" ); my $spec2 = Lire::ReportSpec->load( $spec->superservice(), $spec->id() ); $self->assert_deep_equals( $spec, $spec2 ); } sub test_create_subreport { my $self = $_[0]; $self->set_up_chart_types(); my $subreport = $self->{'simple_spec'}->create_subreport(); $self->assert_isa( 'Lire::Report::Subreport', $subreport ); $self->assert_str_equals( 'test-spec', $subreport->type() ); $self->assert_str_equals( 'test-spec.i', $subreport->id() ); $self->assert_num_equals( 1, scalar @{$subreport->chart_configs()} ); $self->assert_str_equals( 'Top 5 report', $subreport->title() ); $self->assert_str_equals( '5', $subreport->description() ); $self->assert_str_equals( 2, scalar $subreport->entries() ); $self->assert_str_equals( 1, ($subreport->entries())[1]->row_idx() ); $self->assert_isa( 'Lire::Report::TableInfo', $subreport->table_info() ); $self->assert_num_equals( 1, $subreport->table_info()->column_info_by_name( 'aggr' )->col_start() ); } sub test_create_subreport_missing { my $self = $_[0]; my $spec = $self->{'simple_spec'}; $spec->mark_missing( 'Bogus reason' ); my $report = new Lire::Report(); my $subreport = $spec->create_subreport( $report ); $self->assert_isa( 'Lire::Report::Subreport', $subreport ); $self->assert_str_equals( 'test-spec', $subreport->type() ); $self->assert_str_equals( 'test-spec.i', $subreport->id() ); $self->assert( $subreport->is_missing(), 'is_missing() should return true' ); $self->assert_str_equals( 'Bogus reason', $subreport->missing_reason() ); } sub test_set_store { my $self = $_[0]; my $spec = new Lire::ReportSpec(); $spec->id( 'test-spec' ); $spec->{'superservice'} = 'test-extended'; my $aggr = new tests::MockAggregator( 'report_spec' => $spec ); $spec->calc_spec( $aggr ); $self->assert_dies( qr/missing 'store' parameter/, sub { $spec->set_store() } ); $self->assert_dies( qr/'store' parameter should be a 'Lire::DlfStore' instance, not 'HASH/, sub { $spec->set_store( {} ) } ); $self->assert_dies( qr/store doesn't contain a 'test-extended' DLF stream/, sub { $spec->set_store( $self->{'store'} ) } ); $spec->{'superservice'} = 'test'; $spec->set_store( $self->{'store'} ); $self->assert_equals( $self->{'store'}, $aggr->{'store'} ); } sub test_new_from_config { my $self = $_[0]; $self->{'cfg'}{'lr_reports_path'} = [ "$self->{'testdir'}/reports" ]; $self->set_up_chart_types(); my $e_spec = Lire::ReportSpec->load( 'test', 'top-dirs2' ); $e_spec->subreport_id( 'my-top-dirs' ); $e_spec->param( 'dirs_to_show' )->value( 10 ); my $list_spec = new Lire::Config::XMLSpecListSpec( 'name' => 'test', 'type' => 'reports' ); my $list = $list_spec->instance(); my $spec = $list_spec->get( 'test:top-dirs2' )->instance(); $spec->get( 'id' )->set( 'my-top-dirs' ); $list->append( $spec ); $e_spec->chart_configs()->[0]->spec()->summary( $spec->spec()->get( 'charts' )->get( 'chart' )->summary() ); $e_spec->chart_configs()->[0]->spec()->description( $spec->spec()->get( 'charts' )->get( 'chart' )->description() ); $self->assert_deep_equals( [ $e_spec ], $list->as_value() ); $spec->get( 'title' )->set( 'My custom title' ); $spec->get( 'dirs_to_show' )->set( 5 ); $spec->get( 'charts' )->get( 0 )->get( 'title' )->set( 'My test chart' ); $e_spec->display_title( 'My custom title' ); $e_spec->param( 'dirs_to_show' )->value( 5 ); $e_spec->chart_configs()->[0]->title( 'My test chart' ); $self->assert_deep_equals( [ $e_spec ], $list->as_value() ); } sub set_up_chart_report_spec { my $self = $_[0]; my $spec = new Lire::ReportSpec(); $self->assert_null( $spec->{'_chart_configs'}, '_chart_configs != undef' ); $spec->superservice( 'test' ); $spec->id( 'test-chart' ); $spec->subreport_id( 'test-chart.0' ); my $outer = new tests::MockAggregator( 'name' => 'outer', 'report_spec' => $spec ); my $inner = new tests::MockAggregator( 'name' => 'inner', 'parent' => $outer, 'report_spec' => $spec ); $inner->ops( [ new tests::MockAggregate( 'name' => 'value1', 'parent' => $inner, 'report_spec' => $spec ), new tests::MockAggregate( 'name' => 'value2', 'parent' => $inner, 'report_spec' => $spec ) ] ); $outer->ops( [ $inner ] ); $spec->calc_spec( $outer ); $self->{'spec'} = $spec; return; } sub test_default_chart_config { my $self = $_[0]; $self->set_up_chart_types(); $self->set_up_chart_report_spec(); my $cfg = new Lire::Report::ChartConfig(); $cfg->basename( 'test-chart.0' ); $cfg->get( 'case_var' )->set( 'outer' ); $self->{'spec'}->charttype( undef ); $self->assert_null( $self->{'spec'}->default_chart_config(), 'default_chart_config != undef' ); $self->{'spec'}->charttype( 'bars' ); $cfg->type( Lire::PluginManager->get_plugin( 'chart_type', 'vbars' ) ); $cfg->type_properties()->get( 'y' )->set( 'value1' ); $self->assert_deep_equals( $cfg, $self->{'spec'}->default_chart_config() ); $self->{'spec'}->charttype( 'histogram' ); $self->assert_deep_equals( $cfg, $self->{'spec'}->default_chart_config() ); $self->{'spec'}->charttype( 'lines' ); $cfg->type( Lire::PluginManager->get_plugin( 'chart_type', 'lines' ) ); $cfg->type_properties()->get( 'y' )->set( 'value1' ); $self->assert_deep_equals( $cfg, $self->{'spec'}->default_chart_config() ); $self->{'spec'}->charttype( 'pie' ); $cfg->type( Lire::PluginManager->get_plugin( 'chart_type', 'pie' ) ); $cfg->type_properties()->get( 'values' )->set( 'value1' ); $self->assert_deep_equals( $cfg, $self->{'spec'}->default_chart_config() ); } sub test_chart_configs { my $self = $_[0]; $self->set_up_chart_types(); $self->set_up_chart_report_spec(); $self->{'spec'}->charttype( undef ); $self->assert_deep_equals( [], $self->{'spec'}->chart_configs() ); $self->{'spec'}->charttype( 'bars' ); $self->assert_deep_equals( [ $self->{'spec'}->default_chart_config() ], $self->{'spec'}->chart_configs() ); $self->assert_null( $self->{'spec'}{'_chart_configs'} ); my $cfg = new Lire::Report::ChartConfig(); $self->{'spec'}->add_chart_config( $cfg ); $self->assert_deep_equals( [ $cfg ], $self->{'spec'}{'_chart_configs'} ); $self->assert_deep_equals( [ $cfg ], $self->{'spec'}->chart_configs() ); } 1;