#!/usr/bin/perl

# This file was copied from the smolder distribution and modified.
# the copyright and license from the original distribution are
# included below.
# 
# Copyright (c) 2007, Michael Peters
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 
#     * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 
#     * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
# 
#     * Neither the name of the Smolder nor the names of its
#     contributors may be used to endorse or promote products derived from
#     this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use File::Spec::Functions qw(catdir catfile splitdir);
use File::Basename;

BEGIN {
    eval { require WWW::Mechanize };
    if( $@ ) {
        warn "\nCannot load WWW::Mechanize. " .
            "\nPlease install it before using smolder_smoke_signal.\n";
        exit(1);
    }
}

=pod

=head1 NAME

smolder_smoke_signal

=head1 SYNOPSIS

    ./bin/smolder_smoke_signal --server smolder.foo.com \
        --username myself --password s3cr3t --file test_report.xml \
        --project MyProject

=head1 DESCRIPTION

Script used to upload a Smoke test report to a running smolder server.
This is extremely useful for scripted/automatic test runs but also
helpful when using a CLI makes things faster.

=head1 OPTIONS

=head2 REQUIRED

=over

=item server

This is the hostname (and port if not 80) of the running Smolder server.

=item project

The name of the Smolder project to use for the upload.

=item file

The name of the file to upload. Please see F<docs/upload_file_format.pod>
for more details about the format that Smolder expects this file to
take.

=back

=head2 OPTIONAL

=over

=item username

The name of the Smolder user to use for the upload.

=item password

The password for the Smolder user given by C<username>.

=item anonymous

This option takes no arguments and indicates that the report should be
submitted anonymously to a public project. Either the anonymous option
is required or C<username> and C<password> are required.

=item architecture

The architecture for the given smoke test run. If none is given
it will use the default architecture for the project.

=item platform

The platform for the given smoke test run. If none is given
it will use the default platform for the project.

=item tags

A comma separated list of tags that are given for this smoke report run.

    ./bin/smolder_smoke_signal --server smolder.foo.com \
        --username myself --password s3cr3t --file test_report.xml \
        --project MyProject --tags "Foo, My Bar"

=item comments

Any comments that you want to associate with the smoke test run.

=item verbose

Print verbose output of our actions to STDOUT.

=cut

# default options
our ( $server, $project, $user, $pw, $anonymous, $file, $arch, $platform, $tags, $comments, $verbose );
my ( $help, $man );

GetOptions(
    'server=s'       => \$server,
    'project=s'      => \$project,
    'username=s'     => \$user,
    'password=s'     => \$pw,
    'anonymous'      => \$anonymous,
    'file=s'         => \$file,
    'architecture=s' => \$arch,
    'platform=s'     => \$platform,
    'tags=s'         => \$tags,
    'comments=s'     => \$comments,
    'verbose!'       => \$verbose,
    'help'           => \$help,
    'man'            => \$man,
  )
  || pod2usage();

if ($help) {
    pod2usage(
        -exitval => 0,
        -verbose => 1,
    );
} elsif ($man) {
    pod2usage(
        -exitval => 0,
        -verbose => 2,
    );
}

# make sure all the required fields are there
_missing_required('server')   unless $server;
_missing_required('project')  unless $project;
_missing_required('username') unless $user or $anonymous;
_missing_required('password') unless $pw   or $anonymous;
_missing_required('file')     unless $file;

# make sure our file is there and is of the right type
if ( -r $file ) {
    unless( $file =~ /\.tar(\.gz)?$/ ) {
        warn "File '$file' is not of the correct type!\n";
        exit(1);
    }
} else {
    warn "File '$file' does not exist, or is not readable!\n";
    exit(1);
}

# try and reach the smolder server
print "Trying to reach Smolder server at $server.\n" if ($verbose);
my $mech     = WWW::Mechanize->new();
my $base_url = "http://$server/app";
$mech->get($base_url);
unless ( $mech->status eq '200' ) {
    warn "Could not reach $server successfully. Received status " . $mech->status . "\n";
    exit(1);
}

my $content; # holds the content of the current page from $mech
if ( $user ) {
# now login
    print "Trying to login with username '$user'.\n" if ($verbose);
    $mech->get( $base_url . '/public_auth/login' );
    my $form = $mech->form_name('login');
    if ( $mech->status ne '200' || !$form ) {
        warn "Could not reach Smolder login form. Are you sure $server is a Smolder server?\n";
        exit(1);
    }
    $mech->set_fields(
                      username => $user,
                      password => $pw,
                  );
    $mech->submit();
    $content = $mech->content;
    if ( $mech->status ne '200' || $content !~ /Welcome \Q$user\E/ ) {
        warn "Could not login with username '$user' and password '$pw'!\n";
        exit(1);
    }
}

# now go to this project's page
printf( "Retrieving project listing for %s\n", $user ? "user '$user'" : 'anonymous' ) if ($verbose);
$mech->get( $base_url . _url_base() );
$content = $mech->content;
$content =~ />\Q$project\E<!--ID:(\d+)-->/;
my $project_id = $1;
if ( $mech->status ne '200' || !$project_id ) {
    warn
      "Could not get your project listing, or you are not a member of the '$project' project!\n";
    exit(1);
}

# now go to the add-smoke-report page for this project
print "Adding smoke report to project '$project' (id: #$project_id).\n" if ($verbose);
$mech->get( $base_url . _url_base() . "/add_report/$project_id" );
$content = $mech->content;
if ( $mech->status ne '200' || $content !~ /New Smoke Report/ ) {
    warn "Could not reach the Add Smoke Report form!\n";
    exit(1);
}
$mech->form_name('add_report');
my %fields = (report_file => $file);
$fields{platform}     = $platform if ($platform);
$fields{architecture} = $arch     if ($arch);
$fields{tags}         = $tags     if ($tags);
$fields{comments}     = $comments if ($comments);
$mech->set_fields(%fields);
$mech->submit();

$content = $mech->content;
if ( $mech->status ne '200' || $content !~ /Recent Smoke Reports/ ) {
    warn "Could not upload smoke report with the given information!\n";
    exit(1);
}
$content =~ /#(\d+) Added/;
my $report_id = $1;

print "\nReport successfully uploaded as #$report_id.\n";

##########################################################
# helper methods
##########################################################
sub _missing_required {
    my $field = shift;
    warn "Missing required field '$field'!\n";
    pod2usage();
}

# returns the piece of the URL that points to the appropriate part of
# the URL structure based on whether this is an anonymous submission
sub _url_base {
    if ( $anonymous ) {
        return '/public_projects';
    } else {
        return '/developer_projects';
    }
}
