#!/usr/bin/env perl # Copyright (C) 2017 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 use 5.010; use strict; use warnings; =head1 NAME coveragerunner_testcocoon - helper script to run coverage analysis after build (tool used is TestCocoon) =head1 SYNOPSIS # Run code coverage analysis $ ./coveragerunner_testcocoon --qtmodule-dir path/to/module --qtmodule-name modulename --qtcoverage-tests_output path/to/output/folder/tests_database.csmes" # Example launch script for a qt module (ie qtbase) # ./coveragerunner_testcocoon --qtmodule-dir path/to/qtbase --qtmodule-name qtbase --qtcoverage-tests_output "$HOME/qtbase/alltests.csmes" # Will generate: # # $HOME/qtbase/qtbase-coverage_report-.xml # $HOME/qtbase/qtbase-coverage_global-.csmes # This script depends on auto tests run with testrunner script. It is designed to integrate with Qt configured with -testcocoon option and testrunner run with the testcocoon plugin loaded. Required qtcoverage-tests_output generated from testrunner. This file is renamed with unique name to avoid overwriting it. =head1 OPTIONS =over =item B<--help> Print this message. =item B<--qtmodule-dir> Required. Path to Qt5 module to analyze. =item B<--qtmodule-name> Required. Name of Qt5 module to analyze. =item B<--qtcoverage-tests-output> Required. Full path to the csmes database gathering tests results. =back =cut use FindBin; use lib "$FindBin::Bin/../lib/perl5"; package QtQA::App::CoverageRunnerTestCocoon; use base qw(QtQA::TestScript); use Carp; use Getopt::Long qw(GetOptionsFromArray); use English qw( -no_match_vars ); use File::Basename; use File::Spec::Functions; use Pod::Usage qw( pod2usage ); use autodie; use POSIX qw/strftime/; use File::Copy; use File::Find::Rule; sub new { my ($class) = @_; my $self = $class->SUPER::new; bless $self, $class; return $self; } sub run { my ($self, @args) = @_; my $qt_gitmodule_dir; my $qt_gitmodule; my $testcocoon_tests_output; GetOptionsFromArray( \@args, 'help|?' => sub { pod2usage(1) }, 'qt-gitmodule-dir=s' => \$qt_gitmodule_dir, 'qt-gitmodule=s' => \$qt_gitmodule, 'qtcoverage-tests-output=s' => \$testcocoon_tests_output, ) || pod2usage(2); my $currentdatetime = strftime('%Y%m%d-%H%M', localtime); if (! -f $testcocoon_tests_output) { confess "$testcocoon_tests_output does not exist. Either the tests have not been run or coverage was not enabled at build time"; } my $coverage_dir = dirname($testcocoon_tests_output); # Named unique output files my $xml_report = $qt_gitmodule . "_coverage_report-" . $currentdatetime . ".xml"; my $csmes_source = $qt_gitmodule . "_coverage_src-" . $currentdatetime . ".csmes"; my $csmes_tests = $qt_gitmodule . "_coverage_unittests-" . $currentdatetime . ".csmes"; my $csmes_global = $qt_gitmodule . "_coverage_global-" . $currentdatetime . ".csmes"; $xml_report = catfile( $coverage_dir, $xml_report ); $csmes_source = catfile( $coverage_dir, $csmes_source ); $csmes_tests = catfile( $coverage_dir, $csmes_tests ); $csmes_global = catfile( $coverage_dir, $csmes_global ); # Rename global tests csmes file with unique name move($testcocoon_tests_output, $csmes_tests) or confess "move $testcocoon_tests_output: $!"; my $qt_git_qtbase_dir = ($qt_gitmodule eq 'qtbase') ? $qt_gitmodule_dir : ($qt_gitmodule eq 'qt5') ? catfile($qt_gitmodule_dir, 'qtbase') : catfile($qt_gitmodule_dir, '..', 'qtbase'); # Get all sources files csmes gathered in a global database (from lib and plugins folder) my $qt_git_qtbase_libdir = catfile($qt_git_qtbase_dir, 'lib'); print "Gather all library files covered in a global database\n"; my $qt_git_qtbase_pluginsdir = catfile($qt_git_qtbase_dir, 'plugins'); my $qt_git_qtbase_importsdir = catfile($qt_git_qtbase_dir, 'imports'); my @allcsmes = File::Find::Rule->file()->name( '*.csmes' )->in($qt_git_qtbase_libdir); push @allcsmes, File::Find::Rule->file()->name( '*.csmes' )->in($qt_git_qtbase_pluginsdir); push @allcsmes, File::Find::Rule->file()->name( '*.csmes' )->in($qt_git_qtbase_importsdir); @allcsmes = map { canonpath($_) } sort(@allcsmes); print "List of all source files in coverage\n"; foreach my $csmes (@allcsmes) { print "$csmes\n"; if (-e $csmes_source) { $self->exe('cmmerge', '--append', "--output=$csmes_source", $csmes ); } else { copy($csmes, $csmes_source) or confess "copy $csmes: $!"; } } print "End of list\n"; # Create global database move($csmes_source, $csmes_global) or confess "move $csmes_source: $!"; # Merge tests into global $self->exe('cmmerge', '--append', "--output=$csmes_global", $csmes_tests ); # Delete the tests csmes to save space. unlink($csmes_tests) or confess "unlink $csmes_tests: $!"; # Generate report $self->exe('cmreport', "--csmes=$csmes_global", "--xml=$xml_report", '--select=.*', '--source=all', '--source-sort=name', '--global=all' ); # Compress global csmes to save space $self->exe('gzip', $csmes_global); # Create hmtl reports my $xml2html = catfile( $FindBin::Bin, 'xml2html_testcocoon.pl' ); $xml2html = 'xml2html_testcocoon' if (defined $ENV{ TESTING_COVERAGERUNNER }); $self->exe($xml2html, '--xml', $xml_report, '--module', $qt_gitmodule, '--output', $coverage_dir, '--include', catfile($qt_gitmodule_dir, 'src'), '--exclude', catfile($qt_gitmodule_dir, 'src', '3rdparty') ); return; } QtQA::App::CoverageRunnerTestCocoon->new( )->run( @ARGV ) if (!caller); 1;