diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 66fd4bb70..190babbdc 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -113,10 +113,18 @@ class MySQLNoTransactionException extends MySQLException } +/** + * @since 2.7.8 3.0.3 3.1.0 N°5538 + */ +class MySQLTransactionNotClosedException extends MySQLException +{ + +} + /** * CMDBSource - * database access wrapper + * database access wrapper * * @package iTopORM */ diff --git a/test/ItopDataTestCase.php b/test/ItopDataTestCase.php index d269bd8a6..890ce611e 100644 --- a/test/ItopDataTestCase.php +++ b/test/ItopDataTestCase.php @@ -119,12 +119,13 @@ class ItopDataTestCase extends ItopTestCase $this->debug("Removing $sClass::$iKey"); $oObject->DBDelete(); } - catch (Exception $e) - { + catch (Exception $e) { $this->debug("Error when removing created objects : $sClass::$iKey. Exception message: ".$e->getMessage()); } } } + + parent::tearDown(); } /** diff --git a/test/ItopTestCase.php b/test/ItopTestCase.php index 455a886fb..16d403cfe 100644 --- a/test/ItopTestCase.php +++ b/test/ItopTestCase.php @@ -25,6 +25,8 @@ namespace Combodo\iTop\Test\UnitTest; * Time: 11:21 */ +use CMDBSource; +use MySQLTransactionNotClosedException; use PHPUnit\Framework\TestCase; use SetupUtils; @@ -47,15 +49,26 @@ class ItopTestCase extends TestCase @include_once '../../../../../../../../approot.inc.php'; } + /** + * @throws \MySQLTransactionNotClosedException see N°5538 + * @since 2.7.8 3.0.3 3.1.0 N°5538 + */ + protected function tearDown(): void + { + parent::tearDown(); + + if (CMDBSource::IsInsideTransaction()) { + // Nested transactions were opened but not finished ! + throw new MySQLTransactionNotClosedException('Some DB transactions were opened but not closed ! Fix the code by adding ROLLBACK or COMMIT statements !', []); + } + } + protected function debug($sMsg) - { - if (DEBUG_UNIT_TEST) - { - if (is_string($sMsg)) - { - echo "$sMsg\n"; - } - else { + { + if (DEBUG_UNIT_TEST) { + if (is_string($sMsg)) { + echo "$sMsg\n"; + } else { /** @noinspection ForgottenDebugOutputInspection */ print_r($sMsg); } diff --git a/test/core/CMDBSource/TransactionsTest.php b/test/core/CMDBSource/TransactionsTest.php index 335ab2938..55b738cc8 100644 --- a/test/core/CMDBSource/TransactionsTest.php +++ b/test/core/CMDBSource/TransactionsTest.php @@ -10,6 +10,7 @@ use CMDBSource; use Combodo\iTop\Test\UnitTest\ItopTestCase; use Exception; use MetaModel; +use MySQLTransactionNotClosedException; /** * @runTestsInSeparateProcesses @@ -230,22 +231,56 @@ class TransactionsTest extends ItopTestCase public function DBUpdateProvider() { return [ - "Normal case" => ['iFailAt' => -1, 'bIsModified' => false], - "ticket_request" => ['iFailAt' => 1, 'bIsModified' => true], + "Normal case" => ['iFailAt' => -1, 'bIsModified' => false], + "ticket_request" => ['iFailAt' => 1, 'bIsModified' => true], "lnkcontacttoticket" => ['iFailAt' => 2, 'bIsModified' => true], - "History 1" => ['iFailAt' => 3, 'bIsModified' => true], - "History 2" => ['iFailAt' => 4, 'bIsModified' => true], - "History 3" => ['iFailAt' => 5, 'bIsModified' => true], - "History 4" => ['iFailAt' => 6, 'bIsModified' => true], - "History 5" => ['iFailAt' => 7, 'bIsModified' => true], - "History 6" => ['iFailAt' => 8, 'bIsModified' => true], - "History 7" => ['iFailAt' => 9, 'bIsModified' => true], - "History 8" => ['iFailAt' => 10, 'bIsModified' => true], - "History 9" => ['iFailAt' => 11, 'bIsModified' => true], - "History 10" => ['iFailAt' => 12, 'bIsModified' => true], - "History 11" => ['iFailAt' => 13, 'bIsModified' => true], - "History 12" => ['iFailAt' => 14, 'bIsModified' => true], - "History 13" => ['iFailAt' => 15, 'bIsModified' => true], + "History 1" => ['iFailAt' => 3, 'bIsModified' => true], + "History 2" => ['iFailAt' => 4, 'bIsModified' => true], + "History 3" => ['iFailAt' => 5, 'bIsModified' => true], + "History 4" => ['iFailAt' => 6, 'bIsModified' => true], + "History 5" => ['iFailAt' => 7, 'bIsModified' => true], + "History 6" => ['iFailAt' => 8, 'bIsModified' => true], + "History 7" => ['iFailAt' => 9, 'bIsModified' => true], + "History 8" => ['iFailAt' => 10, 'bIsModified' => true], + "History 9" => ['iFailAt' => 11, 'bIsModified' => true], + "History 10" => ['iFailAt' => 12, 'bIsModified' => true], + "History 11" => ['iFailAt' => 13, 'bIsModified' => true], + "History 12" => ['iFailAt' => 14, 'bIsModified' => true], + "History 13" => ['iFailAt' => 15, 'bIsModified' => true], ]; } + + /** + * @return void + * @doesNotPerformAssertions + */ + public function testTransactionOpenedThenClosed() + { + CMDBSource::Query('START TRANSACTION;'); + CMDBSource::Query('COMMIT;'); + } + + /** + * This will throw an exception in the tearDown method. + * This cannot be detected nor by `@expectedException` nor `expectException` method, so we have a specific tearDown impl + * + * @return void + * @doesNotPerformAssertions + */ + public function testTransactionOpenedNotClosed() + { + CMDBSource::Query('START TRANSACTION;'); + } + + protected function tearDown(): void + { + try { + parent::tearDown(); + } + catch (MySQLTransactionNotClosedException $e) { + if ($this->getName() === 'testTransactionOpenedNotClosed') { + $this->debug('Executing the testTransactionOpenNoClose method throws a '.MySQLTransactionNotClosedException::class.' exception in tearDown'); + } + } + } } \ No newline at end of file diff --git a/test/core/apcEmulationTest.php b/test/core/apcEmulationTest.php index abc1296c4..ac088f5a6 100644 --- a/test/core/apcEmulationTest.php +++ b/test/core/apcEmulationTest.php @@ -50,6 +50,7 @@ class apcEmulationTest extends ItopTestCase public function tearDown(): void { apc_clear_cache(); + parent::tearDown(); } public function testBasic() diff --git a/test/core/dictApcuTest.php b/test/core/dictApcuTest.php index de6e6623c..3207c12ca 100644 --- a/test/core/dictApcuTest.php +++ b/test/core/dictApcuTest.php @@ -116,6 +116,8 @@ PHP; } rmdir(APPROOT."env-$this->sEnvName".DIRECTORY_SEPARATOR."dictionaries"); rmdir(APPROOT."env-$this->sEnvName"); + + parent::tearDown(); } public function InitLangIfNeeded_NoApcProvider(){ diff --git a/test/core/dictTest.php b/test/core/dictTest.php index 8d66ae5f8..f27473eff 100644 --- a/test/core/dictTest.php +++ b/test/core/dictTest.php @@ -81,6 +81,8 @@ PHP; } rmdir(APPROOT."env-$this->sEnvName".DIRECTORY_SEPARATOR."dictionaries"); rmdir(APPROOT."env-$this->sEnvName"); + + parent::tearDown(); } /** diff --git a/test/core/iTopConfigParserTest.php b/test/core/iTopConfigParserTest.php index 6934db62e..e7772ec6c 100644 --- a/test/core/iTopConfigParserTest.php +++ b/test/core/iTopConfigParserTest.php @@ -33,7 +33,7 @@ class iTopConfigParserTest extends ItopTestCase public function tearDown(): void { - parent::tearDown(); // TODO: Change the autogenerated stub + parent::tearDown(); if ($this->conf_exists) { rename($this->tmpSavePath, $this->sConfigPath); }