use strictures 1; package Test::Irssi::Driver; use Moose; use lib $ENV{HOME} . "/projects/poe/lib"; use POE qw( Wheel::ReadWrite Wheel::Run Filter::Stream ); use POSIX; use feature qw/say/; use Data::Dump qw/dump/; has 'parent' => ( is => 'ro', isa => 'Test::Irssi', required => 1, ); has 'headless' => ( is => 'rw', isa => 'Bool', default => 0, ); sub START { my ($self, $kernel, $heap) = @_[OBJECT, KERNEL, HEAP]; $kernel->alias_set("IrssiTestDriver"); $self->log("Start handler called"); $self->save_term_settings($heap); # Set a signal handler. $kernel->sig(CHLD => "got_sigchld"); $self->make_raw_terminal; my @stdio_options = ( InputHandle => \*STDIN, OutputHandle => \*STDOUT, InputEvent => "got_terminal_stdin", Filter => POE::Filter::Stream->new(), ); $self->log("stdio options: " . dump(@stdio_options)); # Start the terminal reader/writer. $heap->{stdio} = POE::Wheel::ReadWrite->new(@stdio_options); $self->log("Created stdio wheel"); my $rows = $self->parent->terminal_height; my $cols = $self->parent->terminal_width; my @program_options = ( Program => $self->parent->irssi_binary, ProgramArgs => ['--noconnect', '--home=' . $self->parent->irssi_homedir ], Conduit => "pty", Winsize => [$rows, $cols, 0, 0], StdoutEvent => "got_child_stdout", StdioFilter => POE::Filter::Stream->new(), ); $self->log("wheel options: " . dump(@program_options)); # Start the asynchronous child process. $heap->{program} = POE::Wheel::Run->new(@program_options); $self->log("Created child run wheel"); $poe_kernel->yield('testing_ready'); } sub STOP { my ($self, $heap) = @_[OBJECT,HEAP]; $self->log("STOP called"); $self->restore_term_settings($heap); $self->parent->_logfile_fh->close(); if (not $self->parent->generate_tap) { $self->parent->summarise_test_results(); } } ### Handle terminal STDIN. Send it to the background program's STDIN. ### If the user presses ^C, then echo a little string sub terminal_stdin { my ($self, $heap, $input) = @_[OBJECT, HEAP, ARG0]; if ($input =~ m/\003/g) { # C-c $input = "/echo I like cakes\n"; } elsif ($input =~ m/\x17/g) { # C-w $input = "/quit\n"; } $heap->{program}->put($input); } ### Handle STDOUT from the child program. sub child_stdout { my ($self, $heap, $input) = @_[OBJECT, HEAP, ARG0]; # process via vt $self->parent->vt->process($input); if (not $self->headless) { # send to terminal $heap->{stdio}->put($input); } } ### Handle SIGCHLD. Shut down if the exiting child process was the ### one we've been managing. sub shutdown { my ($self, $heap, $kernel) = @_[OBJECT, HEAP, KERNEL]; $self->log("Shutdown called"); $heap->{program}->kill(15); $kernel->alias_remove("IrssiTestDriver"); } sub CHILD { my ($self, $heap, $child_pid) = @_[OBJECT, HEAP, ARG1]; if ($child_pid == $heap->{program}->PID) { delete $heap->{program}; delete $heap->{stdio}; } return 0; } sub setup { my $self = shift; my @states = ( object_states => [ $self => { _start => 'START', _stop => 'STOP', got_sigchld => 'CHILD', got_terminal_stdin => 'terminal_stdin', got_child_stdout => 'child_stdout', got_delay => 'timer_expired', create_delay => 'timer_created', testing_ready => 'testing_ready', test_complete => 'test_complete', execute_test => 'execute_test', shutdown => 'shutdown', } ] ); $self->log("creating root session"); POE::Session->create(@states); $self->log("session created"); } sub testing_ready { my ($self) = $_[OBJECT]; # begin by fetching a test from the pending queue. $self->log("Starting to run tests"); $self->log("-" x 80); $self->parent->run_test; } sub execute_test { my ($self, $heap, $kernel, $test) = @_[OBJECT,HEAP, KERNEL, ARG0]; # do some stuff here to evaluate it. $test->evaluate_test; } sub test_complete { my ($self, $kernel) = @_[OBJECT, KERNEL]; $self->parent->complete_test; if ($self->parent->tests_remaining) { $self->parent->run_test; } # otherwise, we're done, and can shutdown. #kernel->yield('shutdown'); } sub timer_created { my ($self, $heap, $kernel, $duration) = @_[OBJECT, HEAP, KERNEL, ARG0]; $kernel->delay(got_delay => $duration); $self->log("Timer created for $duration"); } sub timer_expired { my ($self, $data) = @_[OBJECT,ARG0]; $self->log("Timeout invoking test again."); $self->parent->active_test->resume_from_timer; } sub save_term_settings { my ($self, $heap) = @_; # Save the original terminal settings so they can be restored later. $heap->{stdin_tio} = POSIX::Termios->new(); $heap->{stdin_tio}->getattr(0); $heap->{stdout_tio} = POSIX::Termios->new(); $heap->{stdout_tio}->getattr(1); $heap->{stderr_tio} = POSIX::Termios->new(); $heap->{stderr_tio}->getattr(2); } sub restore_term_settings { my ($self, $heap) = @_; $heap->{stdin_tio}->setattr (0, TCSANOW); $heap->{stdout_tio}->setattr(1, TCSANOW); $heap->{stderr_tio}->setattr(2, TCSANOW); } sub make_raw_terminal { my ($self) = @_; # Put the terminal into raw input mode. Otherwise discrete # keystrokes will not be read immediately. my $tio = POSIX::Termios->new(); $tio->getattr(0); my $lflag = $tio->getlflag; $lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG); $tio->setlflag($lflag); my $iflag = $tio->getiflag; $iflag &= ~(BRKINT | INPCK | ISTRIP | IXON); $tio->setiflag($iflag); my $cflag = $tio->getcflag; $cflag &= ~(CSIZE | PARENB); $tio->setcflag($cflag); $tio->setattr(0, TCSANOW); } sub log { my ($self, $msg) = @_; my $fh = $self->parent->_logfile_fh; $fh->say($msg); } __PACKAGE__->meta->make_immutable; no Moose;