#!/usr/bin/perl -w
#############################################################################
##
## Copyright (C) 2012-2013 BogDan Vatra <bogdan@kde.org>
## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
## Contact: http://www.qt-project.org/legal
##
## This file is part of the test suite of the Qt Toolkit.
##
## $QT_BEGIN_LICENSE:LGPL$
## Commercial License Usage
## Licensees holding valid commercial Qt licenses may use this file in
## accordance with the commercial license agreement provided with the
## Software or, alternatively, in accordance with the terms contained in
## a written agreement between you and Digia.  For licensing terms and
## conditions see http://qt.digia.com/licensing.  For further information
## use the contact form at http://qt.digia.com/contact-us.
##
## GNU Lesser General Public License Usage
## Alternatively, this file may be used under the terms of the GNU Lesser
## General Public License version 2.1 as published by the Free Software
## Foundation and appearing in the file LICENSE.LGPL included in the
## packaging of this file.  Please review the following information to
## ensure the GNU Lesser General Public License version 2.1 requirements
## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
##
## In addition, as a special exception, Digia gives you certain additional
## rights.  These rights are described in the Digia Qt LGPL Exception
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
##
## GNU General Public License Usage
## Alternatively, this file may be used under the terms of the GNU
## General Public License version 3.0 as published by the Free Software
## Foundation and appearing in the file LICENSE.GPL included in the
## packaging of this file.  Please review the following information to
## ensure the GNU General Public License version 3.0 requirements will be
## met: http://www.gnu.org/copyleft/gpl.html.
##
##
## $QT_END_LICENSE$
##
#############################################################################

use Cwd;
use Cwd 'abs_path';
use File::Basename;
use File::Temp 'tempdir';
use File::Path 'remove_tree';
use Getopt::Long;
use Pod::Usage;

### default options
my @stack = cwd;
my $device_serial=""; # "-s device_serial";
my $packageName="org.qtproject.qt5.android.tests";
my $intentName="$packageName/org.qtproject.qt5.android.QtActivity";
my $jobs = 4;
my $testsubset = "";
my $man = 0;
my $help = 0;
my $make_clean = 0;
my $deploy_qt = 0;
my $time_out=400;
my $android_sdk_dir = "$ENV{'ANDROID_SDK_ROOT'}";
my $android_ndk_dir = "$ENV{'ANDROID_NDK_ROOT'}";
my $ant_tool = `which ant`;
chomp $ant_tool;
my $strip_tool="";
my $readelf_tool="";
GetOptions('h|help' => \$help
            , man => \$man
            , 's|serial=s' => \$device_serial
            , 't|test=s' => \$testsubset
            , 'c|clean' => \$make_clean
            , 'd|deploy' => \$deploy_qt
            , 'j|jobs=i' => \$jobs
            , 'sdk=s' => \$android_sdk_dir
            , 'ndk=s' => \$android_ndk_dir
            , 'ant=s' => \$ant_tool
            , 'strip=s' => \$strip_tool
            , 'readelf=s' => \$readelf_tool
            , 'testcase=s' => \$testcase
            ) or pod2usage(2);
pod2usage(1) if $help;
pod2usage(-verbose => 2) if $man;

my $adb_tool="$android_sdk_dir/platform-tools/adb";
system("$adb_tool devices") == 0 or die "No device found, please plug/start at least one device/emulator\n"; # make sure we have at least on device attached

$device_serial = "-s $device_serial" if ($device_serial);
$testsubset="/$testsubset" if ($testsubset);

$strip_tool="$android_ndk_dir/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86/bin/arm-linux-androideabi-strip" unless($strip_tool);
$readelf_tool="$android_ndk_dir/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86/bin/arm-linux-androideabi-readelf"  unless($readelf_tool);
$readelf_tool="$readelf_tool -d -w ";

sub dir
{
#    print "@stack\n";
}

sub pushd ($)
{
    unless ( chdir $_[0] )
    {
        warn "Error: $!\n";
        return;
    }
    unshift @stack, cwd;
    dir;
}

sub popd ()
{
    @stack > 1 and shift @stack;
    chdir $stack[0];
    dir;
}


sub waitForProcess
{
    my $process=shift;
    my $action=shift;
    my $timeout=shift;
    my $sleepPeriod=shift;
    $sleepPeriod=1 if !defined($sleepPeriod);
    print "Waiting for $process ".$timeout*$sleepPeriod." seconds to";
    print $action?" start...\n":" die...\n";
    while ($timeout--)
    {
        my $output = `$adb_tool $device_serial shell ps 2>&1`; # get current processes
        #FIXME check why $output is not matching m/.*S $process\n/ or m/.*S $process$/ (eol)
        my $res=($output =~ m/.*S $process/)?1:0; # check the procress
        if ($action == $res)
        {
            print "... succeed\n";
            return 1;
        }
        sleep($sleepPeriod);
        print "timeount in ".$timeout*$sleepPeriod." seconds\n"
    }
    print "... failed\n";
    return 0;
}

my $src_dir_qt=abs_path(dirname($0)."/../../..");
my $quadruplor_dir="$src_dir_qt/tests/auto/android";
my $qmake_path="$src_dir_qt/bin/qmake";
my $tests_dir="$src_dir_qt/tests$testsubset";
my $temp_dir=tempdir(CLEANUP => 1);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my $output_dir=$stack[0]."/".(1900+$year)."-$mon-$mday-$hour:$min";
mkdir($output_dir);
my $sdk_api=0;
my $output = `$adb_tool $device_serial shell getprop`; # get device properties
if ($output =~ m/.*\[ro.build.version.sdk\]: \[(\d+)\]/)
{
    $sdk_api=int($1);
    $sdk_api=5 if ($sdk_api>5 && $sdk_api<8);
    $sdk_api=9 if ($sdk_api>9);
}

sub reinstallQuadruplor
{
    pushd($quadruplor_dir);
    system("$android_sdk_dir/tools/android update project -p . -t android-10")==0 or die "Can't update project ...\n";
    system("$ant_tool uninstall clean debug install")==0 or die "Can't install Quadruplor\n";
    system("$adb_tool $device_serial shell am start -n $intentName"); # create application folders
    waitForProcess($packageName,1,10);
    waitForProcess($packageName,0,20);
    popd();
}
sub killProcess
{
    reinstallQuadruplor;
# #### it seems I'm too idiot to use perl regexp
#     my $process=shift;
#     my $output = `$adb_tool $device_serial shell ps 2>&1`; # get current processes
#     $output =~ s/\r//g; # replace all "\r" with ""
#     chomp($output);
#     print $output;
#     if ($output =~ m/^.*_\d+\s+(\d+).*S $process/) # check the procress
#     {
#         print("Killing $process PID:$1\n");
#         system("$adb_tool $device_serial shell kill $1");
#         waitForProcess($process,0,20);
#     }
#     else
#     {
#         print("Can't kill the process $process\n");
#     }
}


sub startTest
{
    my $libs = shift;
    my $mainLib = shift;
    my $openGL = ((shift)?"true":"false");
    system("$adb_tool $device_serial shell am start -n $intentName --ez needsOpenGl $openGL --es extra_libs \"$libs\" --es lib_name \"$mainLib\""); # start intent
    #wait to start
    return 0 unless(waitForProcess($packageName,1,10));
    #wait to stop
    unless(waitForProcess($packageName,0,$time_out,5))
    {
        killProcess($packageName);
        return 1;
    }
    my $output_file = shift;
    system("$adb_tool $device_serial pull /data/data/$packageName/app_files/output.xml $output_dir/$output_file");
    return 1;
}

sub needsOpenGl
{
    my $app=$readelf_tool.shift.' |grep -e "^.*(NEEDED).*Shared library: \[libQtOpenGL\.so\]$"';
    my $res=`$app`;
    chomp $res;
    return $res;
}

########### delpoy qt libs ###########
if ($deploy_qt)
{

    pushd($src_dir_qt);
    mkdir("$temp_dir/lib");
    my @libs=`find lib -name *.so`; # libs must be handled diferently
    foreach (@libs)
    {
        chomp;
        print ("cp -L $_ $temp_dir/lib\n");
        system("cp -L $_ $temp_dir/lib");
    }
    system("cp -L $android_ndk_dir/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a/libgnustl_shared.so $temp_dir/lib");
    system("cp -a plugins $temp_dir");
    system("cp -a imports $temp_dir");
    system("cp -a qml $temp_dir");
    pushd($temp_dir);
    system("find -name *.so | xargs $strip_tool --strip-unneeded");
    popd;
    system("$adb_tool $device_serial shell rm -r /data/local/tmp/qt"); # remove old qt libs
    system("$adb_tool $device_serial push $temp_dir /data/local/tmp/qt"); # copy newer qt libs
    popd;
}

########### build & install quadruplor ###########
reinstallQuadruplor;

########### build qt tests and benchmarks ###########
pushd($tests_dir);
print "Building $tests_dir \n";
system("make distclean") if ($make_clean);
system("$qmake_path CONFIG-=QTDIR_build -r") == 0 or die "Can't run qmake\n"; #exec qmake
system("make -j$jobs") == 0 or warn "Can't build all tests\n"; #exec make

my $testsFiles = "";
if ($testcase) {
    $testsFiles=`find . -name libtst_$testcase.so`; # only tests
} else {
    $testsFiles=`find . -name libtst_*.so`; # only tests
}

foreach (split("\n",$testsFiles))
{
    chomp; #remove white spaces
    pushd(abs_path(dirname($_))); # cd to application dir
    system("make INSTALL_ROOT=$temp_dir install"); # install the application to temp dir
    system("$adb_tool $device_serial shell rm -r /data/data/$packageName/app_files/*"); # remove old data
    system("$adb_tool $device_serial push $temp_dir /data/data/$packageName/app_files"); # copy
    my $application=basename(cwd);
    my $output_name=dirname($_);
    $output_name =~ s/\.//;   # remove first "." character
    $output_name =~ s/\///;   # remove first "/" character
    $output_name =~ s/\//_/g; # replace all "/" with "_"
    $output_name=$application unless($output_name);
    $time_out=5*60/5; # 5 minutes time out for a normal test
    if (-e "$temp_dir/libtst_bench_$application.so")
    {
        $time_out=5*60/5; # 10 minutes for a benchmark
        $application = "bench_$application";
    }

    if (-e "$temp_dir/libtst_$application.so")
    {
        if (needsOpenGl("$temp_dir/libtst_$application.so"))
        {
             startTest("/data/local/tmp/qt/plugins/platforms/android/libqtforandroidGL.so", "/data/data/$packageName/app_files/libtst_$application.so", 1
                        , "$output_name.xml") or warn "Can't run $application ...\n";
        }
        else
        {
             startTest("/data/local/tmp/qt/plugins/platforms/android/libqtforandroid.so", "/data/data/$packageName/app_files/libtst_$application.so", 0
                        , "$output_name.xml") or warn "Can't run $application stopping tests ...\n";
        }
    }
    else
    {   #ups this test application doesn't respect name convention
        warn "$application test application doesn't respect name convention please fix it !\n";
    }
    popd();
    remove_tree( $temp_dir, {keep_root => 1} );
}
popd();

__END__

=head1 NAME

Script to run all qt tests/benchmarks to an android device/emulator

=head1 SYNOPSIS

runtests.pl [options]

=head1 OPTIONS

=over 8

=item B<-s --serial = serial>

Device serial number. May be empty if only one device is attached.

=item B<-t --test = test_subset>

Tests subset (e.g. benchmarks, auto, auto/qbuffer, etc.).

=item B<-d --deploy>

Deploy current qt libs.

=item B<-c --clean>

Clean tests before building them.

=item B<-j --jobs = number>

Make jobs when building tests.

=item B<--sdk = sdk_path>

Android SDK path.

=item B<--ndk = ndk_path>

Android NDK path.

=item B<--ant = ant_tool_path>

Ant tool path.

=item B<--strip = strip_tool_path>

Android strip tool path, used to deploy qt libs.

=item B<--readelf = readelf_tool_path>

Android readelf tool path, used to check if a test application uses qt OpenGL.

=item B<-h  --help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=back

=head1 DESCRIPTION

B<This program> will run all qt tests/benchmarks to an android device/emulator.

=cut
