The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Selenium::Server;

use 5.008001;
use strict;
use warnings;
use Carp qw(croak);
use LWP::UserAgent;
use Test::TCP;
use File::Slurp qw(write_file);
use File::Temp;
use File::Spec;

our $VERSION = '0.02';
$VERSION = eval $VERSION;

my $ua = LWP::UserAgent->new;

our $LATEST_VERSION;
sub latest_version {
    my $class = shift;

    unless ($LATEST_VERSION) {
        my $body = $ua->get('http://code.google.com/p/selenium/downloads/list')->content;
        my ($version) = $body =~ /selenium-server-standalone-(\d+.\d+.\d+).jar/i;
        $LATEST_VERSION = $version;
    }

    $LATEST_VERSION;
}

sub download {
    my ($class, $version, $path) = @_;

    my $name = "selenium-server-standalone-${version}.jar";
    my $url  = "http://selenium.googlecode.com/files/${name}";

    my $res = $ua->get($url);
    write_file($path, { binmode => ':raw' }, $res->content);
}

sub new {
    my ($class, %args) = @_;

    my $jar = exists $args{jar} ? $args{jar} : do {
        my $version = exists $args{version} ? $args{version} : $class->latest_version;

        my $dir  = File::Spec->tmpdir;
        my $name = "selenium-server-standalone-${version}.jar";
        my $path = File::Spec->catfile($dir, $name);

        $class->download($version, $path) unless -e $path;
        $path;
    };

    my $self = bless { jar => $jar }, $class;

    if ($args{auto_start}) {
        $self->start;
    }

    return $self;
}

sub jar {
    my $self = shift;
    return $self->{jar} || '';
}

sub host {
    my $self = shift;
    return $self->{server} ? '127.0.0.1' : '';
}

sub port {
    my $self = shift;
    return $self->{server} ? $self->{server}->port : '';
}

sub start {
    my ($self, $args) = @_;

    my $server = Test::TCP->new(
        code => sub {
            my $port = shift;

            my $fh = File::Temp->new(UNLINK => 1);
            open STDOUT, '>&', $fh or croak "dup(2) failed:$!";
            open STDERR, '>&', $fh or croak "dup(2) failed:$!";

            my $cmd = sprintf 'java -jar "%s" %s -port %s', $self->jar, ($args || ''), $port;
            system $cmd;
        },
    );
    $server->start;

    $self->{server} = $server;
}

sub stop {
    my $self = shift;

    if ($self->{server}) {
        my ($host, $port) = ($self->host, $self->port);
        my $url = "http://${host}:${port}/selenium-server/driver/?cmd=shutDownSeleniumServer";
        $ua->get($url);

        delete $self->{server};
    }
}

sub DESTROY {
    my $self = shift;
    $self->stop;
}

1;

=head1 NAME

Selenium::Server - A wrapper of selenium-server-standalone.jar

=head1 SYNOPSIS

  use Selenium::Server;

  # (default) download and use latest version jar
  my $server = Selenium::Server->new;
  # specify jar
  my $server = Selenium::Server->new(jar => '/path/to/selenium-server.jar');
  # specify version
  my $server = Selenium::Server->new(version => '2.11.0');

  $server->start;
  # with arguments
  $server->start('-timeout 60 -trustAllSSLCertificates');

  my $host = $server->host; # '127.0.0.1'
  my $port = $server->port;

  $server->stop;

=head1 DESCRIPTION

Selenium::Server is a wrapper of Selenium RemoteWebDriver Server;
selenium-server-standalone-{version}.jar file.

=head1 METHODS

=over 4

=item * new()

Creates a selenium-server wrapper instance.

=item * start($args)

Starts selenium-server, with $args if specified.

=item * stop()

Stops selenium-server.

=item * host()

Returns selenium-server host. (127.0.0.1)

=item * port()

Returns selenium-server port.

=item * jar()

Returns selenium-server JAR file path.

=item * latest_version()

Returns selenium-server latest version string.

=item * download($version, $path)

Downloads selenium-server jar file specified by $version to $path.

=back

=head1 AUTHOR

NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

L<Alien::SeleniumRC>

L<http://selenium.googlecode.com/svn/trunk/rb/lib/selenium/server.rb>

=cut