83a5694dc2
Qt copyrights are now in The Qt Company, so we could update the source code headers accordingly. In the same go we should also fix the links to point to qt.io. Outdated header.LGPL removed (use header.LGPL21 instead) Old header.LGPL3 renamed to header.LGPL3-COMM to match actual licensing combination. New header.LGPL-COMM taken in the use file which were using old header.LGPL3 (src/plugins/platforms/android/extract.cpp) Added new header.LGPL3 containing Commercial + LGPLv3 + GPLv2 license combination Change-Id: I6f49b819a8a20cc4f88b794a8f6726d975e8ffbe Reviewed-by: Matti Paaso <matti.paaso@theqtcompany.com>
556 lines
18 KiB
Perl
Executable File
556 lines
18 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
#############################################################################
|
|
##
|
|
## Copyright (C) 2012-2013 BogDan Vatra <bogdan@kde.org>
|
|
## Copyright (C) 2015 The Qt Company Ltd.
|
|
## Contact: http://www.qt.io/licensing/
|
|
##
|
|
## This file is part of the test suite of the Qt Toolkit.
|
|
##
|
|
## $QT_BEGIN_LICENSE:LGPL21$
|
|
## 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 The Qt Company. For licensing terms
|
|
## and conditions see http://www.qt.io/terms-conditions. For further
|
|
## information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
|
|
## Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
## LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
## following information to ensure the GNU Lesser General Public License
|
|
## requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
##
|
|
## As a special exception, The Qt Company gives you certain additional
|
|
## rights. These rights are described in The Qt Company LGPL Exception
|
|
## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
##
|
|
## $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;
|
|
use XML::Simple;
|
|
use Term::ANSIColor;
|
|
|
|
### default options
|
|
my @stack = cwd;
|
|
my $device_serial=""; # "-s device_serial";
|
|
my $deployqt_device_serial=""; # "-device device_serial";
|
|
my $log_out="xml";
|
|
my $max_runtime = 5;
|
|
my $className="org.qtproject.qt5.android.bindings.QtActivity";
|
|
my $jobs = 4;
|
|
my $testsubset = "";
|
|
my $man = 0;
|
|
my $help = 0;
|
|
my $make_clean = 0;
|
|
my $stop_on_fail = 0;
|
|
my $time_out=400;
|
|
my $android_toolchain_version = "4.8";
|
|
my $host_arch = "linux-x86";
|
|
my $android_sdk_dir = "$ENV{'ANDROID_SDK_ROOT'}";
|
|
my $android_ndk_dir = "$ENV{'ANDROID_NDK_ROOT'}";
|
|
my $android_to_connect = "$ENV{'ANDROID_DEVICE'}";
|
|
my $ant_tool = `which ant`;
|
|
my $silent = 0;
|
|
chomp $ant_tool;
|
|
my $strip_tool="";
|
|
my $readelf_tool="";
|
|
# for ci usage
|
|
my @failures = '';
|
|
my $total_tests = 0;
|
|
my $total_failed = 0;
|
|
my $failed_insignificants = 0;
|
|
my $ci_use = 0;
|
|
my $start = time();
|
|
my $uninstall = 0;
|
|
|
|
GetOptions('h|help' => \$help
|
|
, man => \$man
|
|
, 's|serial=s' => \$device_serial
|
|
, 't|test=s' => \$testsubset
|
|
, 'c|clean' => \$make_clean
|
|
, 'j|jobs=i' => \$jobs
|
|
, 'logtype=s' => \$log_out
|
|
, 'runtime=i' => \$max_runtime
|
|
, 'sdk=s' => \$android_sdk_dir
|
|
, 'ndk=s' => \$android_ndk_dir
|
|
, 'toolchain=s' => \$android_toolchain_version
|
|
, 'host=s' => \$host_arch
|
|
, 'ant=s' => \$ant_tool
|
|
, 'strip=s' => \$strip_tool
|
|
, 'readelf=s' => \$readelf_tool
|
|
, 'testcase=s' => \$testcase
|
|
, 'f|fail' => sub { $stop_on_fail = 1 }
|
|
, 'silent' => sub { $silent = 1 }
|
|
, 'ci' => sub { $ci_use = 1 }
|
|
, 'uninstall' => sub { $uninstall = 1 }
|
|
) or pod2usage(2);
|
|
pod2usage(1) if $help;
|
|
pod2usage(-verbose => 2) if $man;
|
|
|
|
if ($ci_use){
|
|
use QMake::Project;
|
|
}
|
|
my $adb_tool="$android_sdk_dir/platform-tools/adb";
|
|
|
|
# For CI. Nodes are connecting to test devices over IP, which is stored to env variable
|
|
if ($android_to_connect ne ""){
|
|
print " Found device to be connected from env: $android_to_connect \n";
|
|
system("$adb_tool disconnect $android_to_connect");
|
|
system("$adb_tool connect $android_to_connect");
|
|
sleep(2);# let it connect
|
|
system("$adb_tool -s $android_to_connect reboot &");# adb bug, it blocks forever
|
|
sleep(15); # wait for the device to come up again
|
|
system("$adb_tool disconnect $android_to_connect");# cleans up the left adb reboot process
|
|
system("$adb_tool connect $android_to_connect");
|
|
$device_serial =$android_to_connect;
|
|
}
|
|
|
|
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
|
|
|
|
$deployqt_device_serial = "--device $device_serial" if ($device_serial);
|
|
$device_serial = "-s $device_serial" if ($device_serial);
|
|
$testsubset="/$testsubset" if ($testsubset);
|
|
|
|
$strip_tool="$android_ndk_dir/toolchains/arm-linux-androideabi-$android_toolchain_version/prebuilt/$host_arch/bin/arm-linux-androideabi-strip" unless($strip_tool);
|
|
$readelf_tool="$android_ndk_dir/toolchains/arm-linux-androideabi-$android_toolchain_version/prebuilt/$host_arch/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;
|
|
}
|
|
|
|
##############################
|
|
# Read possible insignificance
|
|
# from pro file
|
|
##############################
|
|
sub check_if_insignificant
|
|
{
|
|
return 0 if ( !$ci_use );
|
|
my $case = shift;
|
|
my $insignificant = 0;
|
|
my $prj = QMake::Project->new( 'Makefile' );
|
|
$insignificant = $prj->test( 'insignificant_test' );
|
|
return $insignificant;
|
|
}
|
|
|
|
##############################
|
|
# Print output from given
|
|
# $testresult.txt file
|
|
##############################
|
|
sub print_output
|
|
{
|
|
my $res_file = shift;
|
|
my $case = shift;
|
|
my $insignificant = shift;
|
|
my $print_all = 0;
|
|
$total_tests++;
|
|
if (-e $res_file) {
|
|
open my $file, $res_file or die "Could not open $res_file: $!";
|
|
while (my $line = <$file>) {
|
|
if ($line =~ m/^FAIL/) {
|
|
print "$line";
|
|
# Pretend to be like the "real" testrunner and print out
|
|
# all steps
|
|
$print_all = 1;
|
|
}
|
|
}
|
|
close $file;
|
|
if ($print_all) {
|
|
# In case we are printing all, the test failed
|
|
system("cat $res_file");
|
|
if ($insignificant) {
|
|
print " Testrunner: $case failed, but it is marked with insignificant_test\n";
|
|
push (@failures ,(basename($case)." [insignificant]"));
|
|
$failed_insignificants++;
|
|
} else {
|
|
$total_failed++;
|
|
push (@failures ,(basename($case)));
|
|
}
|
|
} else {
|
|
my $cmd = "sed -n 'x;\$p' ${res_file}";
|
|
my $summary = qx(${cmd});
|
|
if ($summary =~ m/^Totals/) {
|
|
print "$summary";
|
|
} else {
|
|
print "Error: The log is incomplete. Looks like you have to increase the timeout.";
|
|
# The test log seems inclomplete, considering the test as failed.
|
|
if ($insignificant) {
|
|
print " Testrunner: $case failed, but it is marked with insignificant_test\n";
|
|
push (@failures ,(basename($case)." [insignificant]"));
|
|
$failed_insignificants++;
|
|
} else {
|
|
$total_failed++;
|
|
push (@failures ,(basename($case)));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if ($insignificant) {
|
|
print " Failed to execute $case, but it is marked with insignificant_test\n";
|
|
push (@failures ,(basename($case)." [insignificant]"));
|
|
$failed_insignificants++;
|
|
} else {
|
|
print "Failed to execute $case \n";
|
|
$total_failed++;
|
|
push (@failures ,(basename($case)));
|
|
}
|
|
}
|
|
}
|
|
|
|
##############################
|
|
# Print summary of test run
|
|
##############################
|
|
|
|
sub print_summary
|
|
{
|
|
my $total = time()-$start;
|
|
my $h = 0;
|
|
my $m = 0;
|
|
my $s = 0;
|
|
my $exit = 0;
|
|
print "=== Timing: =================== TEST RUN COMPLETED! ============================\n";
|
|
if ($total > 60*60) {
|
|
$h = int($total/60/60);
|
|
$s = int($total - $h*60*60);
|
|
|
|
$m = int($s/60);
|
|
$s = 0;
|
|
print "Total: $h hours $m minutes\n";
|
|
} elsif ($total > 60) {
|
|
$m = int($total/60);
|
|
$s = int($total - $m*60);
|
|
print "Total: $m minutes $s seconds\n";
|
|
} else {
|
|
$s = int($total);
|
|
print "Total: $s seconds\n";
|
|
}
|
|
|
|
print "=== Failures: ==================================================================";
|
|
foreach my $failed (@failures) {
|
|
print $failed."\n";
|
|
$exit = 1;
|
|
}
|
|
print "=== Totals: ".$total_tests." tests, ".($total_tests-$total_failed).
|
|
" passes, ".$failed_insignificants.
|
|
" insignificant fails ======================\n";
|
|
return $exit;
|
|
}
|
|
|
|
|
|
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" if (!$silent);
|
|
print $action?" start...\n":" die...\n" if (!$silent);
|
|
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" if (!$silent);
|
|
return 1;
|
|
}
|
|
sleep($sleepPeriod);
|
|
print "timeount in ".$timeout*$sleepPeriod." seconds\n" if (!$silent);
|
|
}
|
|
print "... failed\n" if (!$silent);
|
|
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 $androiddeployqt_path="$src_dir_qt/bin/androiddeployqt";
|
|
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);
|
|
unlink("latest");
|
|
system(" ln -s $output_dir latest");
|
|
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 checkXMLOutput
|
|
{
|
|
print color 'bold red';
|
|
my $fileName = shift;
|
|
my $XMLOutput = eval { XMLin($fileName, ForceArray => 1) };
|
|
if (!defined($XMLOutput)) {
|
|
print "Can't parse the $fileName file, probably the test crased.\n";
|
|
print color 'reset';
|
|
die "Stopping\n" if $stop_on_fail;
|
|
return;
|
|
}
|
|
my $testName = $XMLOutput->{name};
|
|
my $fail = 0;
|
|
while (my($node_key, $node_valule) = each (%{$XMLOutput})) {
|
|
next if $node_key ne "TestFunction";
|
|
while (my($function_key, $function_valule) = each (%{$node_valule})) {
|
|
while (my($test_key, $test_valule) = each (%{$function_valule})) {
|
|
next if $test_key ne "Incident";
|
|
for my $incident (@{$test_valule}) {
|
|
if (($incident->{type} ne "pass") && ($incident->{type} ne "xfail")) {
|
|
print "test $testName::$function_key failed $incident->{file}:$incident->{line}\n";
|
|
$fail = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
print color 'reset';
|
|
die "Stopping\n" if $stop_on_fail and $fail;
|
|
}
|
|
|
|
sub startTest
|
|
{
|
|
my $testName = shift;
|
|
my $packageName = "org.qtproject.example.tst_$testName";
|
|
my $intentName = "$packageName/org.qtproject.qt5.android.bindings.QtActivity";
|
|
my $output_file = shift;
|
|
my $insignificance = shift;
|
|
my $get_xml= 0;
|
|
my $get_txt= 0;
|
|
my $testLib ="";
|
|
if ($log_out eq "xml") {
|
|
$testLib="-o /data/data/$packageName/output.xml,xml";
|
|
$get_xml = 1;
|
|
} elsif ($log_out eq "txt") {
|
|
$testLib="-o /data/data/$packageName/output.txt,txt";
|
|
$get_txt = 1;
|
|
} else {
|
|
$testLib="-o /data/data/$packageName/output.xml,xml -o /data/data/$packageName/output.txt,txt";
|
|
$get_xml = 1;
|
|
$get_txt = 1;
|
|
}
|
|
|
|
my $cmd="${adb_tool} ${device_serial} shell am start -e applicationArguments \"${testLib}\" -n ${intentName}";
|
|
my $res = qx(${cmd});
|
|
print $res if (!$silent);
|
|
#wait to start (if it has not started and quit already)
|
|
waitForProcess($packageName,1,10);
|
|
|
|
#wait to stop
|
|
unless(waitForProcess($packageName,0,$time_out,5))
|
|
{
|
|
#killProcess($packageName);
|
|
print "Someone should kill $packageName\n";
|
|
system("$adb_tool $device_serial uninstall $packageName") if ($uninstall);
|
|
return 1;
|
|
}
|
|
|
|
# Wait for three seconds to allow process to write all data
|
|
sleep(3);
|
|
|
|
system("$adb_tool $device_serial pull /data/data/$packageName/output.xml $output_dir/$output_file.xml") if ($get_xml);
|
|
|
|
system("$adb_tool $device_serial pull /data/data/$packageName/output.txt $output_dir/$output_file.txt") if ($get_txt);
|
|
if ($get_txt){
|
|
print "Tesresults for $packageName:\n";
|
|
my $insig =
|
|
print_output("$output_dir/$output_file.txt", $packageName, $insignificance);
|
|
}
|
|
system("$adb_tool $device_serial uninstall $packageName") if ($uninstall);
|
|
|
|
checkXMLOutput("$output_dir/$output_file.xml") if ($get_xml);
|
|
return 1;
|
|
}
|
|
|
|
########### build qt tests and benchmarks ###########
|
|
pushd($tests_dir);
|
|
print "Building $tests_dir \n";
|
|
system("make distclean") if ($make_clean);
|
|
system("$qmake_path -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
|
|
my $insig = check_if_insignificant();
|
|
my $cmd="make INSTALL_ROOT=${temp_dir} install";
|
|
my $res = qx(${cmd});
|
|
print $res if (!$silent);
|
|
my $application=basename(cwd);
|
|
if ($silent) {
|
|
$cmd="$androiddeployqt_path --install ${deployqt_device_serial} --output ${temp_dir} --deployment debug --verbose --input android-libtst_${application}.so-deployment-settings.json >/dev/null 2>&1";
|
|
} else {
|
|
$cmd="$androiddeployqt_path --install ${deployqt_device_serial} --output ${temp_dir} --deployment debug --verbose --input android-libtst_${application}.so-deployment-settings.json";
|
|
}
|
|
$res = qx(${cmd});
|
|
print $res if (!$silent);
|
|
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=$max_runtime*60/5; # 5 minutes time out for a normal test
|
|
|
|
$applicationLibrary = `find $temp_dir -name libtst_bench_$application.so`;
|
|
|
|
if ($applicationLibrary)
|
|
{
|
|
$time_out=5*60/5; # 10 minutes for a benchmark
|
|
$application = "bench_$application";
|
|
}
|
|
else
|
|
{
|
|
$applicationLibrary = `find $temp_dir -name libtst_$application.so`;
|
|
}
|
|
|
|
if (!$applicationLibrary)
|
|
{
|
|
print "Can't find application binary libtst_$application.so in $temp_dir!\n";
|
|
}
|
|
else
|
|
{
|
|
startTest($application, "$output_name", $insig) or warn "Can't run $application ...\n";
|
|
}
|
|
|
|
popd();
|
|
remove_tree( $temp_dir, {keep_root => 1} );
|
|
}
|
|
print_summary() if ($ci_use);
|
|
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<-f --fail>
|
|
|
|
Stop the script when test fails. Default 0
|
|
|
|
=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<-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<--logtype = xml|txt|both>
|
|
|
|
The format of log file, default is xml.
|
|
|
|
=item B<--runtime = minutes>
|
|
|
|
The timeout period before stopping individual tests from running.
|
|
|
|
=item B<-silent>
|
|
|
|
Suppress output of system commands.
|
|
|
|
=item B<-ci>
|
|
|
|
Enables checking if test is insignificant or not. Also prints test
|
|
summary after all tests has been executed.
|
|
|
|
=item B<-uninstall>
|
|
|
|
Uninstalls the test after has been executed.
|
|
|
|
=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
|