#!/usr/bin/perl -w
# Copyright 2009-2013 Bernhard M. Wiedemann
# Copyright 2012-2020 SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later
#

=head1 SYNOPSIS

isotovideo [OPTIONS] [TEST PARAMETER]

Parses command line parameters, vars.json and tests the given assets/ISOs.

=head1 OPTIONS

=over 4

=item B<-d, --debug>

Enable direct output to STDERR instead of autoinst-log.txt

=item B<--workdir=>

isotovideo will chdir to that directory on startup

=item B<--color=[yes|no]>

Enable or disable color output explicitly. Defaults to "yes". Alternatively
ANSI_COLORS_DISABLED or NO_COLOR can be set to any value to disable colors.

=item B<-v, --version>

Show the current program version and test API version

=item B<-e, -?, --exit-status-from-test-results>

isotovideo will exit with status 1 when a test module fails.

=item B<-h, -?, --help>

Show this help.

=back

=head1 TEST PARAMETERS

All additional command line arguments specified in the C<key=value> format are
parsed as test parameters which take precedence over the settings in the
vars.json file. Lower case key names are transformed into upper case
automatically for convenience.

=head1 EXIT CODES

=over 4

=item B<0 - SUCCESS>

isotovideo successfully executed a complete run. In the case of C<--exit-status-from-test-results> flag additionally all test modules passed or softfailed.

=item B<1 - ERROR>

An error ocurred during the test execution related to a test backend failure.

=item B<100 - NO TEST MODULES SCHEDULED>

No test module was scheduled.

This exit code can only be reported when invoked with the
C<--exit-status-from-test-results> flag.

=item B<101 - TEST MODULES FAILED>

At least one test module did not end with result "ok" or "softfail".

This exit code can only be reported when invoked with the
C<--exit-status-from-test-results> flag.

=back

=cut

use Mojo::Base -strict, -signatures;
use autodie ':all';
no autodie 'kill';

use constant {
    EXIT_STATUS_OK => 0,
    EXIT_STATUS_ERR => 1,
};

# Avoid "Subroutine JSON::PP::Boolean::(0+ redefined" warnings
# Details: https://progress.opensuse.org/issues/90371
use JSON::PP;

use FindBin '$RealBin';

my $installprefix;    # $bmwqemu::topdir

BEGIN {
    # the following line is modified during make install
    $installprefix = undef;

    my $topdir = "$RealBin/.." || '..';
    $installprefix ||= $topdir;
    unshift @INC, $installprefix;
}

use log qw(diag);
use Getopt::Long;
use Mojo::File qw(curfile);
use Mojo::IOLoop::ReadWriteProcess::Session 'session';
Getopt::Long::Configure("no_ignore_case");
use OpenQA::Isotovideo::Interface;
use OpenQA::Isotovideo::Runner;
use OpenQA::Isotovideo::Utils qw(git_rev_parse spawn_debuggers handle_generated_assets);

my %options;

# global exit status
my $RETURN_CODE = EXIT_STATUS_ERR;

# abstraction providing many helpers and function to run the select-loop
my $runner;

sub usage ($r) {
    $RETURN_CODE = $r;
    eval { require Pod::Usage; Pod::Usage::pod2usage($r) };
    die "cannot display help, install perl(Pod::Usage)\n" if $@;
}

sub _get_version_string () {
    my $thisversion = git_rev_parse(curfile->dirname);
    return "Current version is $thisversion [interface v$OpenQA::Isotovideo::Interface::version]";
}

sub version () {
    print _get_version_string() . "\n";
    $RETURN_CODE = EXIT_STATUS_OK;
    exit 0;
}

sub handle_shutdown () {
    # terminate/kill the command server and let it inform its websocket clients before
    $runner->stop_commands('test execution ended');
    if ($runner->testfd) {
        # unusual shutdown
        $RETURN_CODE = EXIT_STATUS_ERR;    # uncoverable statement
        CORE::close $runner->testfd;    # uncoverable statement
        $runner->stop_autotest();    # uncoverable statement
    }
    diag 'isotovideo ' . ($RETURN_CODE ? 'failed' : 'done');
    my $clean_shutdown = $runner->handle_shutdown(\$RETURN_CODE);
    bmwqemu::load_vars();    # read calculated variables from backend and tests
    $RETURN_CODE = handle_generated_assets($runner->command_handler, $clean_shutdown) unless $RETURN_CODE;
}

GetOptions(\%options, 'debug|d', 'workdir=s', 'color=s', 'help|h|?', 'version|v', 'exit-status-from-test-results|e') or usage(1);
usage(0) if $options{help};
version() if $options{version};

session->enable;
session->enable_subreaper;

my $color = $options{color} // 'yes';
# User setting has preference, see https://no-color.org/
delete $ENV{NO_COLOR} if $color eq 'yes';
# Term::ANSIColor honors this variable
$ENV{ANSI_COLORS_DISABLED} = 1 if $color eq 'no';

chdir $options{workdir} if $options{workdir};

# make sure all commands coming from the backend will not be in the
# developers's locale - but a defined english one. This is SUSE's
# default locale
$ENV{LC_ALL} = 'en_US.UTF-8';
$ENV{LANG} = 'en_US.UTF-8';

eval {
    diag(_get_version_string());
    $log::direct_output = $options{debug};    # enable debug default when started from a tty
    $bmwqemu::topdir = $installprefix;
    $runner = OpenQA::Isotovideo::Runner->new;
    $runner->init(@ARGV);
    spawn_debuggers;
    $runner->handle_commands;
    $RETURN_CODE = EXIT_STATUS_OK;
    $runner->run;    # enter the main loop: process messages from autotest, command server and backend
    $RETURN_CODE = $runner->exit_code_from_test_results if $options{'exit-status-from-test-results'};
    handle_shutdown;
};
if (my $error = $@) {
    log::fctwarn $error, 'main';
    bmwqemu::serialize_state(component => 'isotovideo', msg => $error);
}

END {
    $runner->backend->stop if $runner and $runner->backend;
    $runner and $runner->stop_commands('test execution ended through exception');
    $runner and $runner->stop_autotest();
    $RETURN_CODE //= 0;    # in case of early exit, e.g. help display
    print "$$: EXIT $RETURN_CODE\n";
    $? = $RETURN_CODE;
}
