Skip to content

Commit

Permalink
Add integration test for SIGTERM handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mneudert committed Aug 13, 2024
1 parent aff7688 commit 9a75dc0
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 8 deletions.
55 changes: 55 additions & 0 deletions config/environment/test.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<?php

use Piwik\Container\Container;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Request;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\Mock\FakeChangesModel;
use Piwik\Tests\Framework\Mock\TestConfig;
use Piwik\Tests\Integration\CronArchiveProcessSignalTest;

return array(

Expand Down Expand Up @@ -125,6 +128,58 @@
}
})),

array('CronArchive.init.start', \Piwik\Di::value(function () {
if ('1' !== getenv(CronArchiveProcessSignalTest::ENV_TRIGGER)) {
return;
}

for ($i = 0; $i < 10; $i++) {
$doStart = (bool) Option::get(CronArchiveProcessSignalTest::OPTION_START);

if ($doStart) {
break;
}

sleep(1);
}

if (!$doStart) {
echo 'Waiting for start option took too long!' . PHP_EOL;
exit(127);
}
})),

array('CronArchive.alterArchivingRequestUrl', \Piwik\Di::value(function (&$url) {
if ('1' !== getenv(CronArchiveProcessSignalTest::ENV_TRIGGER)) {
return;
}

$url .= '&' . CronArchiveProcessSignalTest::ENV_TRIGGER . '=1';
})),

array('API.CoreAdminHome.archiveReports', \Piwik\Di::value(function () {
$request = Request::fromRequest();

if (!$request->getBoolParameter(CronArchiveProcessSignalTest::ENV_TRIGGER, false)) {
return;
}

for ($i = 0; $i < 10; $i++) {
$doStart = (bool) Option::get(CronArchiveProcessSignalTest::OPTION_ARCHIVE);

if ($doStart) {
break;
}

sleep(1);
}

if (!$doStart) {
echo 'Waiting for archive option took too long!' . PHP_EOL;
exit(127);
}
})),

array('Updater.checkForUpdates', \Piwik\DI::value(function () {
try {
@\Piwik\Filesystem::deleteAllCacheOnUpdate();
Expand Down
10 changes: 2 additions & 8 deletions core/CliMulti.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ public function runAsSuperUser($runAsSuperUser = true)
public function kill(): void
{
$this->wasKilled = true;

foreach ($this->processes as $process) {
$process->killProcess();
}
Expand Down Expand Up @@ -196,13 +195,8 @@ private function start($piwikUrls)
private function executeUrlCommand($cmdId, $url, $numUrls)
{
if ($this->supportsAsync) {
if ($numUrls === 1) {
$output = new StaticOutput($cmdId);
$this->executeSyncCli($url, $output);
} else {
$output = new Output($cmdId);
$this->executeAsyncCli($url, $output, $cmdId);
}
$output = new Output($cmdId);
$this->executeAsyncCli($url, $output, $cmdId);
} else {
$output = new StaticOutput($cmdId);
$this->executeNotAsyncHttp($url, $output);
Expand Down
129 changes: 129 additions & 0 deletions tests/PHPUnit/Integration/CronArchiveProcessSignalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/

namespace Piwik\Tests\Integration;

use Piwik\CliMulti\CliPhp;
use Piwik\Common;
use Piwik\DataAccess\Model;
use Piwik\Db;
use Piwik\Option;
use Piwik\Tests\Fixtures\OneVisit;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;

/**
* @group Archiver
* @group CronArchive
* @group CronArchiveProcessSignal
*/
class CronArchiveProcessSignalTest extends IntegrationTestCase
{
public const ENV_TRIGGER = 'MATOMO_TEST_CRON_ARCHIVE_PROCESS_SIGNAL';

public const OPTION_ARCHIVE = 'CronArchiveProcessSignalTest.archive';
public const OPTION_START = 'CronArchiveProcessSignalTest.start';

/**
* @var OneVisit
*/
public static $fixture;

public function testSigterm(): void
{
if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
$this->markTestSkipped('signal test cannot run without ext-pcntl');
}

$cliPhp = new CliPhp();
$phpBinary = $cliPhp->findPhpBinary();

// prepending "exec" is required
// otherwise the proc_get_status pid is off by one
$command = sprintf('exec %s %s/tests/PHPUnit/proxy/console core:archive', $phpBinary, PIWIK_INCLUDE_PATH);

$processPipes = [];
$process = proc_open(
$command,
[
['pipe', 'r'],
['pipe', 'w'], // stdout
['pipe', 'w'], // stderr
],
$processPipes,
null,
[
self::ENV_TRIGGER => '1'
],
[
'suppress_errors' => true,
'bypass_shell' => true
]
);

$processStatus = proc_get_status($process);

self::assertTrue($processStatus['running']);
self::assertNotNull($processStatus['pid']);

Option::set(self::OPTION_START, true);

$dataAccessModel = new Model();

for ($i = 0; $i < 10; $i++) {
$invalidationsInProgress = $dataAccessModel->getInvalidationsInProgress(self::$fixture->idSite);

if ([] !== $invalidationsInProgress) {
break;
}

sleep(1);
}

self::assertNotEmpty($invalidationsInProgress);
self::assertSame(4, $this->getArchiveInvalidationCount(self::$fixture->idSite));

//Option::set(self::OPTION_ARCHIVE, true);

$processStatus = proc_get_status($process);

self::assertTrue($processStatus['running']);

proc_terminate($process, SIGTERM);

for ($i = 0; $i < 10; $i++) {
$processStatus = proc_get_status($process);

if (!$processStatus['running']) {
break;
}

sleep(1);
}

self::assertFalse($processStatus['running']);
self::assertSame(0, $processStatus['exitcode']);

$invalidationsInProgress = $dataAccessModel->getInvalidationsInProgress(self::$fixture->idSite);

self::assertEmpty($invalidationsInProgress);
self::assertSame(4, $this->getArchiveInvalidationCount(self::$fixture->idSite));
}

private function getArchiveInvalidationCount(int $idSite): int
{
return Db::fetchOne(
'SELECT COUNT(*) FROM ' . Common::prefixTable('archive_invalidations') . ' WHERE idsite = ?',
[
$idSite
]
);
}
}

CronArchiveProcessSignalTest::$fixture = new OneVisit();

0 comments on commit 9a75dc0

Please sign in to comment.