package tests::UITest;

use strict;

use base qw/Test::Unit::TestSuite/;

sub name {
    return "Lire::LireUI Tests";
}

sub include_tests {
    return qw/tests::UITest::init
              tests::UITest::api
            /;
}


package tests::UITest::init;

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

use Curses;
use Lire::UI;
use Lire::Test::CursesUIDriver;

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

    $self->{'driver'} = new Lire::Test::CursesUIDriver();
    $self->{'driver'}->setup_curses_ui();

    $self->{'ui'} = bless { '_ui' => new Curses::UI() }, 'Lire::UI';
    $self->{'driver'}->set_curses_ui( $self->{'ui'}{'_ui'} );

    return;
}

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

    $self->{'driver'}->teardown_curses_ui();

    return;
}

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

    $self->{'ui'}->_create_menubar();
    my $mbar = $self->{'ui'}{'_ui'}->getobj( 'menubar' );
    $self->assert_isa( 'Curses::UI::Menubar', $mbar );
    my @menus = map { $_->{'-label'} } @{$mbar->{'-menu'}};
    $self->assert_deep_equals( [ 'Lire', 'Store' ], \@menus );

    my @menu_cbs = ( [ \&Lire::UI::_lire_about_cb,
                       \&Lire::UI::_lire_prefs_cb,
                       \&Lire::UI::_lire_quit_cb ],
                     [ \&Lire::UI::_store_new_cb,
                       \&Lire::UI::_store_open_cb ] );
    for ( my $menu_idx = 0; $menu_idx < @menu_cbs; $menu_idx++ ) {
        my $menu_cb = $menu_cbs[$menu_idx];
        for ( my $i=0; $i < @$menu_cb; $i++ ) {
            my $menu_def = $mbar->{'-menu'}[$menu_idx]{'-submenu'}[$i];
            $self->assert_str_equals( $menu_def->{'-value'},
                                      $menu_cb->[$i],
                                      "'$menu_def->{'-label'}''s callback isn't set properly" );
        }
    }

    $self->assert_str_equals( '__routine_' . \&Lire::UI::_focus_menubar_cb,
                              $self->{'ui'}{'_ui'}{'-bindings'}{KEY_F(10)} );

}

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

    my $ui = $self->{'ui'};
    $ui->_create_menubar();
    $ui->_create_quickstart_win();

    $ui->{'_ui'}->getobj( 'quickstart_win' )->focus();

    my $mbar = $ui->{'_ui'}->getobj( 'menubar' );
    $self->assert_equals( 0, $mbar->{'-focus'} );
    Lire::UI::_focus_menubar_cb( $ui->{'_ui'} );
    $self->assert_equals( 1, $mbar->{'-focus'} );
}

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

    $self->{'ui'}->_create_quickstart_win();

    my $win = $self->{'ui'}{'_ui'}->getobj( 'quickstart_win' );
    $self->assert_isa( 'Curses::UI::Window', $win );
    my $viewer = $win->getobj( 'text_viewer' );
    $self->assert_isa( 'Curses::UI::TextViewer',  $viewer );
}

package tests::UITest::api;

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

use Lire::Config;
use Lire::UI;
use Lire::Test::CursesUIDriver;
use Lire::Config::TypeSpec;
use Lire::Config::ConfigFile;
use Lire::Utils qw/tempdir create_file/;

use Curses::UI::Dialog::Status;

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

    $self->tests::TestStoreFixture::init();

    return $self;
}

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

    $self->set_up_test_store();

    $self->{'driver'} = new Lire::Test::CursesUIDriver();
    $self->{'driver'}->setup_curses_ui();

    $self->{'ui'} = new Lire::UI();
    $self->{'driver'}->set_curses_ui( $self->{'ui'}->curses_ui() );

    $self->{'cfg'}{'_lr_config_spec'} = $self->lire_default_config_spec();
    $self->{'cfg'}{'lr_templates_path'} = [];

    $self->{'cfg_file'} = "$self->{'tmpdir'}/.lire/config.xml";
    mkdir "$self->{'tmpdir'}/.lire";

    $self->{'parsed_cfg'} =
      new Lire::Config::ConfigFile( 'spec'=> $self->{'cfg'}{'_lr_config_spec'},
                                    'filename' => $self->{'cfg_file'} );

    $self->{'parsed_cfg'}->save();

    return;
}

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

    my $spec = $self->{'cfg'}{'_lr_config_spec'};
    my $jobs = $spec->get( 'import_jobs' )->instance();
    $self->{'cfg'}{ 'import_jobs' } = $jobs;

    my $job = $spec->get( 'import_jobs' )->get( 'import_job' )->instance();
    $job->get( 'name' )->set( 'aJob' );
    $job->get( 'filter' )->set( '' );
    $job->get( 'log_encoding' )->set( 'UTF-8' );
    $job->get( 'log_file' )->set( 'a file' );
    $job->get( 'period' )->set( 'hourly' );
    $job->get( 'service' )->set_plugin( 'www' );

    $jobs->append( $job );
}

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

    $self->{'driver'}->teardown_curses_ui();

    return;
}

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

    my $ui = $self->{'ui'};
    $self->assert_isa( 'Lire::UI', $ui );
    $self->assert_isa( 'Curses::UI', $ui->{'_ui'} );

    $self->assert_str_equals( $ui, $ui->{'_ui'}->userdata() );

    $self->assert_not_null( $ui->{'_ui'}->getobj( 'quickstart_win' ),
                            "was _create_quickstart_win() called?" );
    $self->assert_not_null( $ui->{'_ui'}->getobj( 'menubar' ),
                            "was _create_menubar() called?" );
}

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

    $self->assert_dies( qr/^Quit$/,
                        sub { $self->{'ui'}->_lire_quit_cb( $self->{'ui'}{'_ui'} )} );
}

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

    my $called = 0;
    $self->{'driver'}->add_event_loop_handler( sub { $called++ } );
    $self->{'driver'}->add_event_loop_handler( sub { die "Quit\n" } );

    my $rc = $self->{'ui'}->mainloop();
    $self->assert_equals( 1, $called );
    $self->assert_equals( 0, $rc );

    my $err_dialog;
    $self->{'driver'}->add_event_loop_handler( sub { die "Error\n" } );
    $self->{'driver'}->add_event_loop_handler( sub { $err_dialog = $_[1]; } );
    $rc = $self->{'ui'}->mainloop();
    $self->assert_equals( 1, $rc );
    $self->assert_isa( "Curses::UI::Dialog::Error", $err_dialog );
    $self->assert_str_equals( $err_dialog->{'-message'},
                              "An error occured: Error\n" );

    my $warn_dialog;
    no warnings 'redefine';
    my $or_status_new = \&Curses::UI::Dialog::Status::new;
     local *Curses::UI::Dialog::Status::new =
       sub() { $warn_dialog = $or_status_new->( @_ ) };
    $self->{'driver'}->add_event_loop_handler( sub { warn "Warning\n";
                                                     die "Quit\n"} );
    $self->{'driver'}->add_event_loop_handler( sub { $warn_dialog = $_[1]; } );
    $rc = $self->{'ui'}->mainloop();
    $self->assert_equals( 0, $rc );
    $self->assert(  "Curses::UI::Dialog::Status", $warn_dialog );
    $self->assert_str_equals( $warn_dialog->{'-message'}, "Warning\n" );
}

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

    my $cui = $self->{'ui'}{'_ui'};
    $cui->userdata( $self->{'ui'} );
    $self->{'ui'}->cleanup();
    $self->assert_null( $self->{'ui'}{'_ui'},
                        "'_ui' attribute should be null" );
    $self->assert_null( $cui->userdata(), "userdata() should be null" );
}

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

    my $driver = $self->{'driver'};
    my $about_dlg = '<undef>';
    $driver->add_event_loop_handler( sub { $about_dlg = $_[1] } );
    Lire::UI::_lire_about_cb( $self->{'ui'}{'_ui'} );

    $self->assert_isa( "Curses::UI::Dialog::Basic",  $about_dlg );
    $self->assert_matches( qr/\(c\) 2000-2004/,
                           $about_dlg->{'-message'} );
    $self->assert_matches( qr/logreport\@logreport.org/,
                           $about_dlg->{'-message'} );

    $self->assert_matches( qr/About/, $about_dlg->{'-title'} );
}

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

    local $ENV{'HOME'} = $self->{'tmpdir'};

    no warnings 'redefine';
    local *Lire::Test::HashConfig::config_files = sub { $self->{'cfg_file'} };
    local *Lire::Test::HashConfig::get_config_file =
      sub { return $self->{'parsed_cfg'} };

    my $driver = $self->{'driver'};
    my $prefs_dlg = '<undef>';
    $driver->add_event_loop_handler( sub { $prefs_dlg = $_[1] } );
    Lire::UI::_lire_prefs_cb( $self->{'ui'}{'_ui'} );

    $self->assert_isa( "Curses::UI::Window", $prefs_dlg );
    my $prefs_ui = $prefs_dlg->userdata() || '<undef>';
    $self->assert_isa( 'Lire::UI::Prefs', $prefs_ui );
}

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

    my $dlg = '<undef>';

    $self->{'driver'}->add_event_loop_handler(
       sub {
           $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( $self->{'tmpdir'} );
           $dlg->getobj('filevalue')->text( 'new_store' );
           $dlg->getobj('buttons')->{'-selected'} = 0;
       } );

    Lire::UI::_store_new_cb( $self->{'ui'}{'_ui'} );
    $self->assert_isa( 'Curses::UI::Dialog::Filebrowser',  $dlg );
    $self->assert( -d $self->{'tmpdir'} . '/new_store',
                   "'$self->{'tmpdir'}/new_store' not created" );

    my $store_win = $self->{'driver'}->top_window();
    $self->assert_str_equals( $self->{'driver'}->find_widget( '/store_win' ),
                              $store_win );
    my $store = $store_win->store() || '<undef>';
    $self->assert_isa( 'Lire::DlfStore',  $store );
    $self->assert_str_equals( "$self->{'tmpdir'}/new_store", $store->path() );
}

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

    create_file( "$self->{'tmpdir'}/new_store", "" );
    $self->{'driver'}->add_event_loop_handler(
       sub {
           my $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( $self->{'tmpdir'} );
           $dlg->getobj('filevalue')->text( 'new_store' );
           $dlg->getobj('buttons')->{'-selected'} = 0;
       } );
    # Acknowledge dialog
    my $dlg = '<undef>';
    $self->{'driver'}->add_event_loop_handler( sub { $dlg = $_[1] } );

    Lire::UI::_store_new_cb( $self->{'ui'}{'_ui'} );
    $self->assert_isa( "Curses::UI::Dialog::Error",  $dlg );
    $self->assert_matches( qr/Error creating store:\n    can't create DLF/,
                         $dlg->{'-message'},);
}

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

    my $dlg = '<undef>';
    $self->{'driver'}->add_event_loop_handler(
       sub {
           $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( $self->{'tmpdir'} );
           $dlg->getobj('filevalue')->text( 'new_store' );
           $dlg->getobj('buttons')->{'-selected'} = 1;
       } );
    Lire::UI::_store_new_cb( $self->{'ui'}{'_ui'} );
    $self->assert( ! -d $self->{'tmpdir'} . '/new_store',
                   "'$self->{'tmpdir'}/new_store' was created unexpectedly" );
    $self->assert_str_equals($self->{'driver'}->find_widget('/quickstart_win'),
                             $self->{'driver'}->top_window() );

    return;
}

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

    $self->{'store'}->close();
    my $dlg = '<undef>';
    $self->{'driver'}->add_event_loop_handler(
       sub {
           $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( $self->{'store'}->path() );
           $dlg->getobj('buttons')->{'-selected'} = 0;
       } );

    Lire::UI::_store_open_cb( $self->{'ui'}{'_ui'} );
    $self->assert_isa( 'Curses::UI::Dialog::Dirbrowser', $dlg );

    my $store_win = $self->{'driver'}->top_window();
    $self->assert_str_equals( $self->{'driver'}->find_widget( '/store_win' ),
                              $store_win );
    my $store = $store_win->store() || '<undef>';
    $self->assert_isa( 'Lire::DlfStore', $store );
    $self->assert_str_equals( $self->{'store'}->path(), $store->path() );
}

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

    $self->{'driver'}->add_event_loop_handler(
       sub {
           my $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( $self->{'store'}->path() );
           $dlg->getobj('buttons')->{'-selected'} = 1;
       } );
    Lire::UI::_store_open_cb( $self->{'ui'}{'_ui'} );
    $self->assert_str_equals($self->{'driver'}->find_widget('/quickstart_win'),
                             $self->{'driver'}->top_window() );

    return;
}

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

    create_file( "$self->{'tmpdir'}/new_store", "" );
    $self->{'driver'}->add_event_loop_handler(
       sub {
           my $dlg = $_[1];
           $dlg->getobj('pathvalue')->text( "$self->{'tmpdir'}/new_store" );
           $dlg->getobj('buttons')->{'-selected'} = 0;
       } );
    # Acknowledge dialog
    my $dlg = '<undef>';
    $self->{'driver'}->add_event_loop_handler( sub { $dlg = $_[1] } );

    Lire::UI::_store_open_cb( $self->{'ui'}{'_ui'} );
    $self->assert_isa( "Curses::UI::Dialog::Error", $dlg );
    $self->assert_matches( qr/Error opening store:\n    DlfStore /,
                         $dlg->{'-message'},);
}

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

    local $ENV{'HOME'} = $self->{'tmpdir'};
    my @files = ( $self->{'cfg_file'} );
    my %parsed_files = ( $self->{'cfg_file'} => 'Success' );

    no warnings 'redefine';
    local *Lire::Test::HashConfig::config_files = sub { return @files };
    local *Lire::Test::HashConfig::add_config_file =
      sub { push @files, $_[1] };
    local *Lire::Test::HashConfig::get_config_file =
      sub { return $parsed_files{$_[1]} };

    unlink $self->{'cfg_file'};
    $self->assert_str_equals( 'Success', $self->{'ui'}->_get_config_file() );
    $self->assert_deep_equals( [ $self->{'cfg_file'} ], \@files );
    $self->assert( !-f $self->{'cfg_file'}, "'$self->{'cfg_file'}' shouldn't exists" );

    @files = ();
    $self->assert_str_equals( 'Success', $self->{'ui'}->_get_config_file() );
    $self->assert_deep_equals( [ $self->{'cfg_file'} ], \@files );
    $self->assert( -s $self->{'cfg_file'}, "empty '$self->{'cfg_file'}'" );
}

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

    $self->{'ui'}->_update_store_menu( 1 );
    my $store_menu = $self->{'driver'}->find_menu_def( 'menubar/Store' );
    $self->assert_deep_equals( [ { '-label' => 'Close',
                                   '-value' => \&Lire::UI::_store_close_cb } ],
                               $store_menu->{'-submenu'} );

    $self->{'ui'}->_update_store_menu( 0 );
    $self->assert_deep_equals( [ { '-label' => 'New...',
                                   '-value' => \&Lire::UI::_store_new_cb },
                                 { '-label' => 'Open...',
                                   '-value' => \&Lire::UI::_store_open_cb } ],
                               $store_menu->{'-submenu'} );
}

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

    $self->{'ui'}->_open_store_window( $self->{'store'} );
    Lire::UI::_store_close_cb( $self->{'ui'}{'_ui'} );

    $self->assert_null( $self->{'ui'}{'_ui'}->getobj( 'store_win' ),
                        'store_win should be closed' );
    $self->assert( $self->{'store'}->is_closed(),
                   'store should have been closed' );
    my $menubar = $self->{'driver'}->find_widget( '/menubar' );
    $self->assert_equals( 'New...',
                          $menubar->{'-menu'}[1]->{'-submenu'}[0]{'-label'} );
}

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

    $self->{'ui'}->_open_store_window( $self->{'store'} );
    my $store_win = $self->{'driver'}->top_window();
    $self->assert_str_equals( $self->{'driver'}->find_widget( '/store_win' ),
                              $store_win );
    $self->assert_isa( 'Lire::UI::StoreWindow', $store_win );
    my $store = $store_win->store() || '<undef>';
    $self->assert_isa( 'Lire::DlfStore',  $store );

    my $menubar = $self->{'driver'}->find_widget( '/menubar' );
    $self->assert_equals( 'Close',
                          $menubar->{'-menu'}[1]->{'-submenu'}[0]{'-label'} );
}

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

    my $value = new Lire::Config::StringSpec( 'name' => 'string' )->instance();

    my $ui = $self->{'ui'};
    my $ok_sub  = sub {
        my $dialog = $_[1];

        $self->assert_str_equals( 'My title', $dialog->title() );
        my $widget = $dialog->getobj( 'widget' );
        $self->assert_isa( 'Lire::UI::Widget', $widget );
        $self->assert_str_equals( $value, $widget->{'value'} );

        my $buttons = $dialog->getobj( 'buttons' );
        $buttons->{'-buttons'}[1]{'-onpress'}->();
    };

    $self->{'driver'}->add_event_loop_handler( $ok_sub );
    $self->assert_num_equals( 1, $ui->edit_value_dialog( $value,
                                                         'My title'  ) );
    my $cancel_sub  = sub {
        my $dialog = $_[1];

        $self->assert_str_equals( 'Editing string', $dialog->title() );

        my $buttons = $dialog->getobj( 'buttons' );
        $buttons->{'-buttons'}[0]{'-onpress'}->();
    };

    $self->{'driver'}->add_event_loop_handler( $cancel_sub );
    $self->assert_num_equals( 0, $ui->edit_value_dialog( $value ) );

    return;
}

1;


syntax highlighted by Code2HTML, v. 0.9.1