The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
use strict;
use lib 'inc';

use FindBin;
use IO::Catch;
use File::Temp qw( tempfile );
use vars qw( %tests $_STDOUT_ $_STDERR_ );
use URI::URL;
use LWP::Simple;

# Catch output:
$SIG{__WARN__} = sub { $main::_STDERR_ .= join '', @_; };
tie *STDOUT, 'IO::Catch', '_STDOUT_' or die $!;
#tie *STDERR, 'IO::Catch', '_STDERR_' or die $!;

# Make HTML::Display do nothing:
BEGIN {
  $ENV{PERL_HTML_DISPLAY_CLASS} = 'HTML::Display::Dump';
  delete $ENV{PAGER};
};
use HTML::Display;

BEGIN {
  %tests = (
    autofill => { requests => 2, lines => [ 'get %s',
                                            'autofill query Fixed foo',
                                            'autofill cat Keep',
                                            'fillout',
                                            'submit' ], location => qr'^%s/formsubmit\?session=1&query=foo&cat=cat_foo&cat=cat_bar$'},
    auth => { requests => 1, lines => [ 'auth user password', 'get %s' ], location => qr'^%s/$' },
    back => { requests => 2, lines => [ 'get %s','open 0','back' ], location => qr'^%s/$' },
    content_save => { requests => 1, lines => [ 'get %s','content tmp.content','eval unlink "tmp.content"'], location => qr'^%s/$' },
    comment => { requests => 1, lines => [ '# a comment','get %s','# another comment' ], location => qr'^%s/$' },
    eval => { requests => 1, lines => [ 'eval "Hello World"', 'get %s','eval "Goodbye World"' ], location => qr'^%s/$' },
    eval_shell => { requests => 1, lines => [ 'get %s', 'eval $self->agent->ct' ], location => qr'^%s/$' },
    eval_sub => { requests => 2, lines => [
						'# Fill in the "date" field with the current date/time as string',
  					'eval sub ::custom_today { "20030511" };',
  					'autofill session Callback ::custom_today',
  					'autofill query Keep',
            'autofill cat Keep',
  					'get %s',
  					'fillout',
  					'eval $self->agent->current_form->value("session")',
  					'submit',
  					'content',
    ], location => qr'^%s/formsubmit\?session=20030511&query=\(empty\)&cat=cat_foo&cat=cat_bar$' },
    eval_multiline => { requests => 2,
    									lines => [ 'get %s',
    							 							 'autofill query Keep',
											           'autofill cat Keep',
    														 'fillout',
    														 'submit',
    														 'eval "Hello World ",
    														        "from ",$self->agent->uri',
    														 'content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar$' },
    form_name => { requests => 2, lines => [ 'get %s','form f','submit' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar$'
            },
    form_num => { requests => 2, lines => [ 'get %s','form 1','submit' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar$'
            },
    formfiller_chars => { requests => 2,
    									lines => [ 'eval srand 0',
											           'autofill cat Keep',
    														 'autofill query Random::Chars size 5 set alpha', 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=[a-zA-Z]{5}&cat=cat_foo&cat=cat_bar$' },
    formfiller_date => { requests => 2,
    									lines => [ 'eval srand 0',
											           'autofill cat Keep',
    														 'autofill query Random::Date string %%Y%%m%%d', 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\d{8}&cat=cat_foo&cat=cat_bar$' },
    formfiller_default => { requests => 2,
    									lines => [ 'autofill query Default foo',
											           'autofill cat Keep',
					    									 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar$' },
    formfiller_fixed => { requests => 2,
    									lines => [ 'autofill query Fixed foo',
											           'autofill cat Keep',
    														 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=foo&cat=cat_foo&cat=cat_bar$' },
    formfiller_keep => { requests => 2,
    									lines => [ 'autofill query Keep',
											           'autofill cat Keep',
    														 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar' },
    formfiller_random => { requests => 2,
    									lines => [ 'autofill query Random foo',
											           'autofill cat Keep',
    														 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=foo&cat=cat_foo&cat=cat_bar' },
    formfiller_re => { requests => 2,
    									lines => [ 'eval srand 0',
											           'autofill cat Keep',
    														 'autofill /qu/ Random::Date string %%Y%%m%%d', 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\d{8}&cat=cat_foo&cat=cat_bar' },
    formfiller_word => { requests => 2,
    									lines => [ 'eval srand 0',
											           'autofill cat Keep',
    														 'autofill query Random::Word size 1', 'get %s', 'fillout','submit','content' ],
    									location => qr'^%s/formsubmit\?session=1&query=\w+&cat=cat_foo&cat=cat_bar' },
    get => { requests => 1, lines => [ 'get %s' ], location => qr'^%s/' },
    get_content => { requests => 1, lines => [ 'get %s', 'content' ], location => qr'^%s/' },
    get_redirect => { requests => 2, lines => [ 'get %sredirect/startpage' ], location => qr'^%s/startpage' },
    get_save => { requests => 4, lines => [ 'get %s','save "/\.save_log_server_test\.tmp$/"' ], location => qr'^%s/' },
    get_value_click => { requests => 2, lines => [ 'get %s','value query foo', 'click submit' ], location => qr'^%s/formsubmit\?session=1&query=foo&submit=Go&cat=cat_foo&cat=cat_bar' },
    get_value_submit => { requests => 2, lines => [ 'get %s','value query foo', 'submit' ], location => qr'^%s/formsubmit\?session=1&query=foo&cat=cat_foo&cat=cat_bar' },
    get_value2_submit => { requests => 2, lines => [
    				'get %s',
    				'value query foo',
    				'value session 2',
    				'submit'
    ], location => qr'^%s/formsubmit\?session=2&query=foo&cat=cat_foo&cat=cat_bar' },
    interactive_script_creation => { requests => 2,
    									lines => [ 'eval @::list=qw(foo bar xxx)',
    														 'eval no warnings qw"redefine once"; *WWW::Mechanize::FormFiller::Value::Ask::ask_value = sub { my $value=shift @::list; push @{$_[0]->{shell}->{answers}}, [ $_[1]->name, $value ]; $value }',
											           'autofill cat Keep',
    														 'get %s',
    														 'fillout',
    														 'submit',
    														 'content' ],
    									location => qr'^%s/formsubmit\?session=foo&query=bar&cat=cat_foo&cat=cat_bar$' },
    open_parm => { requests => 2, lines => [ 'get %s','open 0','content' ], location => qr'^%s/test$' },
    open_re => { requests => 2, lines => [ 'get %s','open "Link foo1.save_log_server_test.tmp"','content' ], location => qr'^%s/foo1.save_log_server_test.tmp$' },
    open_re2 => { requests => 2, lines => [ 'get %s','open "/foo1/"','content' ], location => qr'^%s/foo1.save_log_server_test.tmp$' },
    open_re3 => { requests => 2, lines => [ 'get %s','open "/Link /foo/"','content' ], location => qr'^%s/foo$' },
    open_re4 => { requests => 2, lines => [ 'get %s','open "/Link \/foo/"','content' ], location => qr'^%s/foo$' },
    open_re5 => { requests => 2, lines => [ 'get %s','open "/Link /$/"','content' ], location => qr'^%s/slash_end$' },
    open_re6 => { requests => 2, lines => [ 'get %s','open "/^/Link$/"','content' ], location => qr'^%s/slash_front$' },
    open_re7 => { requests => 2, lines => [ 'get %s','open "/^/Link in slashes//"','content' ], location => qr'^%s/slash_both$' },
    reload => { requests => 2, lines => [ 'get %s','reload','content' ], location => qr'^%s/$' },
    reload_2 => { requests => 3, lines => [ 'get %s','open "/Link \/foo/"','reload','content' ], location => qr'^%s/foo$' },
    tick => { requests => 2,
              lines => [ 'get %s','tick cat cat_foo','submit','content' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar$' },
    tick_all => { requests => 2,
              lines => [ 'get %s','tick cat','submit','content' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_foo&cat=cat_bar&cat=cat_baz$' },
    timeout => { requests => 1, lines => [ 'timeout 60', 'get %s', 'content' ], location => qr'^%s/' },
    ua_get => { requests => 1, lines => [ 'ua foo/1.1', 'get %s' ], location => qr'^%s/$' },
    ua_get_content => { requests => 1, lines => [ 'ua foo/1.1', 'get %s', 'content' ], location => qr'^%s/$' },
    untick => { requests => 2,
              lines => [ 'get %s','untick cat cat_foo','submit','content' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)&cat=cat_bar$' },
    untick_all => { requests => 2,
              lines => [ 'get %s','untick cat','submit','content' ],
              location => qr'^%s/formsubmit\?session=1&query=\(empty\)$' },
  );

  eval {
    require HTML::TableExtract;
    $tests{get_table} = { requests => 1, lines => [ 'get %s','table' ], location => qr'^%s/$' };
    $tests{get_table_params} = { requests => 1, lines => [ 'get %s','table Col2 Col1' ], location => qr'^%s/$' };
  };

  # To ease zeroing in on tests
  if (@ARGV) {
      my $re = join "|", @ARGV;
      for (sort keys %tests) {
           delete $tests{$_} unless /$re/o;
        };
    };
};

use Test::More tests => 1 + (scalar keys %tests)*8;
BEGIN {
  # Disable all ReadLine functionality
  $ENV{PERL_RL} = 0;
  require LWP::UserAgent;
  #my $old = \&LWP::UserAgent::request;
  #print STDERR $old;
  #*LWP::UserAgent::request = sub {print STDERR "LWP::UserAgent::request\n"; goto &$old };
  use_ok('WWW::Mechanize::Shell');
};

SKIP: {
diag "Loading HTTP::Daemon";
eval { require HTTP::Daemon; };
skip "HTTP::Daemon required to test script/code identity",(scalar keys %tests)*8
  if ($@);
# require Test::HTTP::LocalServer; # from inc
use Test::HTTP::LocalServer; # from inc

# We want to be safe from non-resolving local host names
delete @ENV{qw(HTTP_PROXY http_proxy CGI_HTTP_PROXY)};

use vars qw( $actual_requests $dumped_requests );
{
  no warnings qw'redefine once';
  my $old_request = *WWW::Mechanize::_make_request{CODE};
  *WWW::Mechanize::_make_request = sub {
    $actual_requests++;
    goto &$old_request;
  };

  *WWW::Mechanize::Shell::status = sub {};
  *WWW::Mechanize::Shell::request_dumper = sub { $dumped_requests++; return 1 };

  #*Hook::LexWrap::Cleanup::DESTROY = sub {
      #print STDERR "Disabling hook.\n";
      #$_[0]->();
  #};
};

diag "Spawning local test server";
my $server = Test::HTTP::LocalServer->spawn();
diag sprintf "on port %s", $server->port;

require LWP::UserAgent;
my $lwp_useragent_request = *LWP::UserAgent::request{CODE};
for my $name (sort keys %tests) {
  $_STDOUT_ = '';
  undef $_STDERR_;
  $actual_requests = 0;
  $dumped_requests = 0;
  my @lines = @{$tests{$name}->{lines}};
  my $requests = $tests{$name}->{requests};

  my $code_port = $server->port;

  my $url = $server->url;
  $url =~ s!/$!!;
  my $result_location = sprintf $tests{$name}->{location}, $url;
  $result_location = qr{$result_location};
  {
      no warnings 'redefine';
      *LWP::UserAgent::request = $lwp_useragent_request;
  };
  my $s = WWW::Mechanize::Shell->new( 'test', rcfile => undef, warnings => undef );
  $s->option("dumprequests",1);
  my @commands;
  eval {
      for my $line (@lines) {
        $line = sprintf $line, $server->url;
        push @commands, $line;
        $s->cmd($line);
      };
  };
  is $@, '', "Commands ran without dieing"
      or do { diag for @commands };
  $s->cmd('eval $self->agent->uri');
  my $code_output = $_STDOUT_;
  diag join( "\n", $s->history )
    unless like($s->agent->uri,$result_location,"Shell moved to the specified url for $name");
  is($_STDERR_,undef,"Shell produced no error output for $name");
  is($actual_requests,$requests,"$requests requests were made for $name");
  is($dumped_requests,$requests,"$requests requests were dumped for $name");
  my $code_requests = $server->get_output;

  # Get a clean start
  my $script_port = $server->port;

  # Modify the generated Perl script to match the new? port
  my $script = join "\n", $s->script;
  s!\b$code_port\b!$script_port!smg for ($script, $code_output);
  #print STDERR "Releasing hook";
  undef $s->{request_wrapper};
  #{
  #  local *WWW::Mechanize::Shell::request_dumper = sub { die };
  #  use HTTP::Request::Common;
  #  $s->agent->request(GET 'http://google.de/');
  #};
  $s->release_agent;
  undef $s;

  # Write the generated Perl script
  my ($fh,$tempname) = tempfile();
  print $fh $script;
  close $fh;

  my ($compile) = `"$^X" -c "$tempname" 2>&1`;
  chomp $compile;
  SKIP: {
    unless (is($compile,"$tempname syntax OK","$name compiles")) {
      $server->get_output;
      diag $script;
      skip "Script $name didn't compile", 2;
    };
    my ($output);
    my $command = qq("$^X" -Iblib/lib "$tempname" 2>&1);
    $output = `$command`;
    is( $output, $code_output, "Output of $name is identical" )
      or diag "Script:\n$script";
    my $script_requests = $server->get_output;
    $code_requests =~ s!\b$code_port\b!$script_port!smg;
    is($code_requests,$script_requests,"$name produces identical queries")
      or diag $script;
  };
  unlink $tempname
    or diag "Couldn't remove tempfile '$name' : $!";
};
# $server->stop;

unlink $_ for (<*.save_log_server_test.tmp>);

};