Revision control

++ #!/usr/bin/env perl
++ use 5.010;
++ use strict;
++ use warnings;
++
++ =head1 NAME
++
++ 01-testrunner-testcocoon.t - test testrunner's 'testcocoon' plugin for code coverage analysis.
++
++ =head1 SYNOPSIS
++
++ perl ./01-testrunner-testcocoon.t
++
++ This test will run the testrunner.pl script with some fake testcocoon processes
++ and verify that the testcocoon plugin generates the expected output.
++
++ =cut
++
++ use Capture::Tiny qw( capture );
++ use Cwd;
++ use English qw( -no_match_vars );
++ use Env::Path;
++ use File::Path qw( mkpath );
++ use File::Spec::Functions;
++ use File::Temp qw( tempdir );
++ use FindBin;
++ use Readonly;
++ use Test::More;
++
++ use lib "$FindBin::Bin/../../lib/perl5";
++ use QtQA::Test::More qw( is_or_like create_mock_command );
++
++ # Directory separator, quoted for regex
++ Readonly my $DS => ($OSNAME eq 'MSWin32') ? q{\\\\} : q{/};
++
++ # Tempdir testcocoon for tests template
++ Readonly my $TEMPDIR_TOPLEVEL => catfile(File::Spec->tmpdir);
++
++ Readonly my $TEMPDIR_TEMPLATE =>
++ qr{\Q$TEMPDIR_TOPLEVEL\E${DS}testcocoon_plugin\..{6}}sm;
++
++ # Commands used multiple times
++
++ Readonly my $MYTEST_DIR => qr{${TEMPDIR_TEMPLATE}${DS}module${DS}mytest}sm;
++
++ Readonly my $GLOBAL_TEST =>
++ qr{$MYTEST_DIR${DS}mytest_global\.csmes}sm;
++
++ Readonly my $COVERAGERUNNER_CMMERGE_OTHERCSMES =>
++ qr{\+ cmmerge --append --output=$GLOBAL_TEST $MYTEST_DIR${DS}mytest\.csmes.*};
++ Readonly my $COVERAGERUNNER_CMMERGE_PLUGIN_WITH_TEST =>
++ qr{\+ cmmerge --append --output=$GLOBAL_TEST ${TEMPDIR_TEMPLATE}${DS}(?:module${DS}\.\.${DS})?qtbase${DS}plugins${DS}plugin1${DS}plugin1\.csmes.*};
++ Readonly my $COVERAGERUNNER_CMCSEXEIMPORT =>
++ qr{\+ cmcsexeimport --csmes=$GLOBAL_TEST --csexe=$MYTEST_DIR${DS}myother\.csexe --title=tc_mytest --policy=merge.*};
++ Readonly my $COVERAGERUNNER_CMCSEXEIMPORT_OTHER =>
++ qr{\+ cmcsexeimport --csmes=$GLOBAL_TEST --csexe=$MYTEST_DIR${DS}mytest\.csexe --title=tc_mytest --policy=merge.*};
++ Readonly my $COVERAGERUNNER_CMMERGE_TEST_WITH_GLOBAL =>
++ qr{\+ cmmerge --append --output=${TEMPDIR_TEMPLATE}${DS}tests\.csmes $GLOBAL_TEST.*};
++
++ # Missing --testcocoon-tests-output required option
++ Readonly my $MISSING_REQUIRED_TESTS_OUTPUT =>
++ qr{internal error: .*${DS}testcocoon\.pm loaded OK, but QtQA::App::TestRunner::Plugin::testcocoon could not be instantiated: Missing required '--testcocoon-tests-output' option at .*${DS}testcocoon\.pm line .*};
++
++ # Missing --testcocoon-qt-gitmodule-dir required option
++ Readonly my $MISSING_REQUIRED_GITDIR =>
++ qr{internal error: .*${DS}testcocoon\.pm loaded OK, but QtQA::App::TestRunner::Plugin::testcocoon could not be instantiated: Invalid or missing required '--testcocoon-qt-gitmodule-dir' option at .*${DS}testcocoon\.pm line .*};
++
++ # Missing --testcocoon-qt-gitmodule required option
++ Readonly my $MISSING_REQUIRED_MODULE =>
++ qr{internal error: .*${DS}testcocoon\.pm loaded OK, but QtQA::App::TestRunner::Plugin::testcocoon could not be instantiated: Missing required '--testcocoon-qt-gitmodule' option at .*${DS}testcocoon\.pm line .*};
++
++ # Cmmerge other csmes
++ Readonly my $CMMERGE_OTHERCSMES =>
++ qr{test success.*
++ $COVERAGERUNNER_CMMERGE_OTHERCSMES}sm;
++
++ # Cmmerge with a plugin
++ Readonly my $CMMERGE_PLUGIN_WITH_TEST =>
++ qr{$CMMERGE_OTHERCSMES
++ cmmerge success other csmes.*
++ $COVERAGERUNNER_CMMERGE_PLUGIN_WITH_TEST}sm;
++
++ # Cmcsexeimport
++ Readonly my $CMCSEXEIMPORT =>
++ qr{$CMMERGE_PLUGIN_WITH_TEST
++ cmmerge success plugin with test.*
++ $COVERAGERUNNER_CMCSEXEIMPORT}sm;
++
++ # Cmcsexeimport other
++ Readonly my $CMCSEXEIMPORT_OTHER =>
++ qr{$CMCSEXEIMPORT
++ cmcsexeimport success.*
++ $COVERAGERUNNER_CMCSEXEIMPORT_OTHER}sm;
++
++ # Cmmerge test into global
++ Readonly my $CMMERGE_TEST_INTO_GLOBAL =>
++ qr{$CMCSEXEIMPORT_OTHER
++ cmcsexeimport other success.*
++ $COVERAGERUNNER_CMMERGE_TEST_WITH_GLOBAL}sm;
++
++ # Successful run
++ Readonly my $TESTSCRIPT_SUCCESS =>
++ qr{$CMMERGE_TEST_INTO_GLOBAL
++ cmmerge success test into global.*}sm;
++
++ sub test_run
++ {
++ my ($params_ref) = @_;
++
++ my @args = @{$params_ref->{ args }};
++ my $expected_stdout = $params_ref->{ expected_stdout };
++ my $expected_stderr = $params_ref->{ expected_stderr };
++ my $expected_success = $params_ref->{ expected_success };
++ my $testname = $params_ref->{ testname } // q{};
++
++ my $status;
++ my ($output, $error) = capture {
++ $status = system( 'perl', "$FindBin::Bin/../testrunner.pl", @args );
++ };
++
++ if ($expected_success) {
++ is ( $status, 0, "$testname exits zero" );
++ }
++ else {
++ isnt( $status, 0, "$testname exits non-zero" );
++ }
++
++ is_or_like( $output, $expected_stdout, "$testname output looks correct" );
++ is_or_like( $error, $expected_stderr, "$testname error looks correct" );
++
++ return;
++ }
++
++ sub test_success
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ create_mock_command(
++ name => 'mytest',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "test success\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmmerge',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmmerge success other csmes\n"},
++ { exitcode => 0, stdout => "cmmerge success plugin with test\n"},
++ { exitcode => 0, stdout => "cmmerge success test into global\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmcsexeimport',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmcsexeimport success\n"},
++ { exitcode => 0, stdout => "cmcsexeimport other success\n"},
++ ],
++ );
++
++ my $initdir = getcwd;
++ my $testdir = catfile( $tempdir, 'module', 'mytest');
++ chdir($testdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'test success',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-qt-gitmodule', 'module', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 1,
++ expected_stdout => $TESTSCRIPT_SUCCESS,
++ expected_stderr => q{},
++ });
++
++ chdir($initdir);
++
++ return;
++ }
++
++ sub test_invalid_tests_output_arg
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++
++ test_run({
++ testname => 'missing test output',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-qt-gitmodule-dir', 'fake_gitmodule_dir', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => q{},
++ expected_stderr => $MISSING_REQUIRED_TESTS_OUTPUT,
++ });
++
++ return;
++ }
++
++ sub test_invalid_qt_gitmodule_dir
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'missing git module dir',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => q{},
++ expected_stderr => $MISSING_REQUIRED_GITDIR,
++ });
++
++ return;
++ }
++
++ sub test_missing_qt_gitmodule
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'missing git module name',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-tests-output', $tests_csmes, '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => q{},
++ expected_stderr => $MISSING_REQUIRED_MODULE,
++ });
++
++ return;
++ }
++
++ sub test_merge_other_csmes_failure
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ create_mock_command(
++ name => 'mytest',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "test success\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmmerge',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 1, stderr => "cmmerge failure other csmes\n"},
++ ],
++ );
++
++ my $initdir = getcwd;
++ my $testdir = catfile( $tempdir, 'module', 'mytest');
++ chdir($testdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'cmmerge other csmes failure',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-qt-gitmodule', 'module', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => $CMMERGE_OTHERCSMES,
++ expected_stderr => qr{cmmerge failure other csmes\n},
++ });
++
++ chdir($initdir);
++
++ return;
++ }
++
++ sub test_cmmerge_plugin_with_test_failure
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ create_mock_command(
++ name => 'mytest',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "test success\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmmerge',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmmerge success other csmes\n"},
++ { exitcode => 1, stderr => "cmmerge failure plugin\n"},
++ ],
++ );
++
++ my $initdir = getcwd;
++ my $testdir = catfile( $tempdir, 'module', 'mytest');
++ chdir($testdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'cmmerge into global plugins failure',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-qt-gitmodule', 'module', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => $CMMERGE_PLUGIN_WITH_TEST,
++ expected_stderr => qr{cmmerge failure plugin\n},
++ });
++
++ chdir($initdir);
++
++ return;
++ }
++
++ sub test_cmcsexeimport_failure
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ create_mock_command(
++ name => 'mytest',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "test success\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmmerge',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmmerge success other csmes\n"},
++ { exitcode => 0, stdout => "cmmerge success plugin with test\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmcsexeimport',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 3, stderr => "cmcsexeimport failure\n"},
++ ],
++ );
++
++ my $initdir = getcwd;
++ my $testdir = catfile( $tempdir, 'module', 'mytest');
++ chdir($testdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'cmcsexeimport failure',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-qt-gitmodule', 'module', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => $CMCSEXEIMPORT,
++ expected_stderr => qr{cmcsexeimport failure\n},
++ });
++
++ chdir($initdir);
++
++ return;
++ }
++
++ sub test_cmmerge_test_into_global_failure
++ {
++ my $tempdir = tempdir( 'testcocoon_plugin.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++ init_test_env($tempdir);
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ create_mock_command(
++ name => 'mytest',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "test success\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmmerge',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmmerge success other csmes\n"},
++ { exitcode => 0, stdout => "cmmerge success plugin with test\n"},
++ { exitcode => 4, stderr => "cmmerge failure test into global\n"},
++ ],
++ );
++
++ create_mock_command(
++ name => 'cmcsexeimport',
++ directory => $tempdir,
++ sequence => [
++ { exitcode => 0, stdout => "cmcsexeimport success\n"},
++ { exitcode => 0, stdout => "cmcsexeimport other success\n"},
++ ],
++ );
++
++ my $initdir = getcwd;
++ my $testdir = catfile( $tempdir, 'module', 'mytest');
++ chdir($testdir);
++
++ my $module_gitdir = catfile( $tempdir, 'module');
++ my $tests_csmes = catfile( $tempdir, 'tests.csmes' );
++
++ test_run({
++ testname => 'cmmerge test into global failure',
++ args => [ '--plugin', 'testcocoon', '--testcocoon-tests-output', $tests_csmes, '--testcocoon-qt-gitmodule-dir', $module_gitdir, '--testcocoon-qt-gitmodule', 'module', '--', "$tempdir/mytest",'testname' ],
++ expected_success => 0,
++ expected_stdout => $CMMERGE_TEST_INTO_GLOBAL,
++ expected_stderr => qr{cmmerge failure test into global\n},
++ });
++
++ chdir($initdir);
++
++ return;
++ }
++
++ sub init_test_env
++ {
++ my ($tempdir) = @_;
++
++ # Create qtbase path
++ my $qtbase_path = catfile($tempdir, 'qtbase');
++ if (! -d $qtbase_path && ! mkpath( $qtbase_path )) {
++ die "mkpath $qtbase_path: $!";
++ }
++
++ # Create "module" path
++ my $gitmodule_path = catfile($tempdir, 'module');
++ if (! -d $gitmodule_path && ! mkpath( $gitmodule_path )) {
++ die "mkpath $gitmodule_path: $!";
++ }
++
++ # Create "mytest" path
++ my $mytest_path = catfile($gitmodule_path, 'mytest');
++ if (! -d $mytest_path && ! mkpath( $mytest_path )) {
++ die "mkpath $mytest_path: $!";
++ }
++
++ # Create qtbase plugins path
++ my $plugins_path = catfile($qtbase_path, 'plugins');
++ if (! -d $plugins_path && ! mkpath( $plugins_path )) {
++ die "mkpath $plugins_path: $!";
++ }
++
++ my $plugin1_path = catfile($plugins_path, 'plugin1');
++ if (! -d $plugin1_path && ! mkpath( $plugin1_path )) {
++ die "mkpath $plugin1_path: $!";
++ }
++
++ # Create csmes for 1 plugin
++ open(my $plugin1, ">", "$plugin1_path/plugin1.csmes") or die $!;
++ close($plugin1);
++
++ # Create current test csexe and csmes
++ open(my $mytestcsmes, ">", "$mytest_path/mytest.csmes") or die $!;
++ close($mytestcsmes);
++ open(my $mytestcsexe, ">", "$mytest_path/mytest.csexe") or die $!;
++ close($mytestcsexe);
++ open(my $myothercsmes, ">", "$mytest_path/myother.csmes") or die $!;
++ close($myothercsmes);
++ open(my $myothercsexe, ">", "$mytest_path/myother.csexe") or die $!;
++ close($myothercsexe);
++ open(my $moccsexe, ">", "$mytest_path/moc.csexe") or die $!;
++ close($moccsexe);
++
++ # Create a tests result database csmes
++ open(my $test, ">", "$tempdir/tests.csmes") or die $!;
++ close($test);
++
++ return;
++ }
++
++ sub run
++ {
++ test_success;
++ test_invalid_tests_output_arg;
++ test_invalid_qt_gitmodule_dir;
++ test_merge_other_csmes_failure;
++ test_cmmerge_plugin_with_test_failure;
++ test_cmcsexeimport_failure;
++ test_cmmerge_test_into_global_failure;
++ test_missing_qt_gitmodule;
++
++ done_testing;
++
++ return;
++ }
++
++ run if (!caller);
++ 1;
++