mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-30 14:08:46 +02:00
CRUD reentrance protection
This commit is contained in:
@@ -6,6 +6,7 @@ use Combodo\iTop\Service\EventData;
|
||||
use Combodo\iTop\Service\EventService;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use ContextTag;
|
||||
use Exception;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
@@ -55,7 +56,10 @@ class EventTest extends ItopTestCase
|
||||
|
||||
public function testNoParameterCallbackFunction()
|
||||
{
|
||||
$sId = EventService::RegisterListener('event', function () { $this->debug("Closure: event received !!!"); self::IncrementCallCount(); });
|
||||
$sId = EventService::RegisterListener('event', function () {
|
||||
$this->debug("Closure: event received !!!");
|
||||
self::IncrementCallCount();
|
||||
});
|
||||
$this->debug("Registered $sId");
|
||||
EventService::FireEvent(new EventData('event'));
|
||||
$this->assertEquals(1, self::$iEventCalls);
|
||||
@@ -81,9 +85,10 @@ class EventTest extends ItopTestCase
|
||||
public function GoodCallbackProvider()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
|
||||
return array(
|
||||
'method' => array(array($oReceiver, 'OnEvent1')),
|
||||
'static' => array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent1'),
|
||||
'method' => array(array($oReceiver, 'OnEvent1')),
|
||||
'static' => array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent1'),
|
||||
'static2' => array(array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver', 'OnStaticEvent1')),
|
||||
);
|
||||
}
|
||||
@@ -291,6 +296,157 @@ class EventTest extends ItopTestCase
|
||||
self::$iEventCalls++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ReentranceProvider
|
||||
*/
|
||||
public function testReentrance($aClasses, $iEventCount)
|
||||
{
|
||||
foreach ($aClasses as $sName => $aClass) {
|
||||
new TestReentrance($sName, $aClass['prio'], $aClass['events'], $aClass['permanent_protection']);
|
||||
}
|
||||
EventService::FireEvent(new EventData('event1', 'main'));
|
||||
$this->assertEquals($iEventCount, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function ReentranceProvider()
|
||||
{
|
||||
return [
|
||||
'1 class' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
],
|
||||
'iEventCount' => 1,
|
||||
],
|
||||
'2 classes - 1' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
],
|
||||
'iEventCount' => 3,
|
||||
],
|
||||
'2 classes - 2' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
],
|
||||
'iEventCount' => 4,
|
||||
],
|
||||
'2 classes - 3' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => true]]],
|
||||
],
|
||||
'iEventCount' => 3,
|
||||
],
|
||||
'3 classes - 1' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class C' => ['prio' => 20, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => true]]],
|
||||
],
|
||||
'iEventCount' => 11,
|
||||
],
|
||||
'3 classes - non permanent' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => false, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class C' => ['prio' => 20, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => false, 'event2' => true], 'listener2' => ['event1' => false, 'event2' => true]]],
|
||||
],
|
||||
'iEventCount' => 12,
|
||||
],
|
||||
'2 classes - loop' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
],
|
||||
'iEventCount' => 4,
|
||||
],
|
||||
'2 classes - loop2' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => false, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['event1' => true, 'event2' => false], 'listener2' => ['event1' => false, 'event2' => false]]],
|
||||
],
|
||||
'iEventCount' => 5,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ReentranceCRUDProvider
|
||||
*/
|
||||
public function testReentranceCRUD($aClasses, $iEventCount)
|
||||
{
|
||||
foreach ($aClasses as $sName => $aClass) {
|
||||
new TestReentranceCRUD($sName, $aClass['prio'], $aClass['events'], $aClass['permanent_protection']);
|
||||
}
|
||||
$oObject = new TestCRUDObject();
|
||||
$oObject->DBInsert('main');
|
||||
$this->assertEquals($iEventCount, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function ReentranceCRUDProvider()
|
||||
{
|
||||
return [
|
||||
'1 class' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
],
|
||||
'iEventCount' => 1,
|
||||
],
|
||||
'2 classes - 1' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
],
|
||||
'iEventCount' => 2,
|
||||
],
|
||||
'2 classes - 2'=> [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
],
|
||||
'iEventCount' => 4,
|
||||
],
|
||||
'2 classes - 3' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => true]]],
|
||||
],
|
||||
'iEventCount' => 3,
|
||||
],
|
||||
'3 classes - 1' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class C' => ['prio' => 20, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => true]]],
|
||||
],
|
||||
'iEventCount' => 11,
|
||||
],
|
||||
'3 classes - non permanent' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => false, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => false]]],
|
||||
'Class C' => ['prio' => 20, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => true], 'listener2' => ['update' => true]]],
|
||||
],
|
||||
'iEventCount' => 12,
|
||||
],
|
||||
'2 classes - loop' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
],
|
||||
'iEventCount' => 4,
|
||||
],
|
||||
'2 classes - loop2' => [
|
||||
'aClasses' => [
|
||||
'Class A' => ['prio' => 0, 'permanent_protection' => false, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
'Class B' => ['prio' => 10, 'permanent_protection' => true, 'events' => ['listener1' => ['update' => false], 'listener2' => ['update' => false]]],
|
||||
],
|
||||
'iEventCount' => 5,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
@@ -298,21 +454,50 @@ class EventTest extends ItopTestCase
|
||||
*/
|
||||
public static function DebugStatic($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
if (DEBUG_UNIT_TEST) {
|
||||
if (is_string($sMsg)) {
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestEventReceiver
|
||||
class TestClassesWithDebug
|
||||
{
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public static function DebugStatic($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST) {
|
||||
if (is_string($sMsg)) {
|
||||
echo "$sMsg\n";
|
||||
} else {
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sMsg
|
||||
*/
|
||||
public function Debug($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST) {
|
||||
if (is_string($sMsg)) {
|
||||
echo "$sMsg\n";
|
||||
} else {
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestEventReceiver extends TestClassesWithDebug
|
||||
{
|
||||
|
||||
// Event callbacks
|
||||
@@ -363,44 +548,201 @@ class TestEventReceiver
|
||||
self::DebugStatic(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public static function DebugStatic($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public function Debug($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestCRUDObject extends TestClassesWithDebug
|
||||
{
|
||||
private static $aEventList = [];
|
||||
|
||||
public function DBInsert($sName)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->FireEvent('insert');
|
||||
}
|
||||
|
||||
public function DBUpdate($sName)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->FireEvent('update');
|
||||
}
|
||||
|
||||
public function FireEvent($sEvent, $aEventData = [])
|
||||
{
|
||||
$aEventData['name'] = $this->sName;
|
||||
$aEventData['object'] = $this;
|
||||
$oEventData = new EventData($sEvent, $this->sName, $aEventData);
|
||||
|
||||
$bFireEventNow = empty(self::$aEventList['event']);
|
||||
self::$aEventList['event'][] = $oEventData;
|
||||
|
||||
$iCount = 0;
|
||||
while ($bFireEventNow) {
|
||||
$sEvent = $oEventData->GetEvent();
|
||||
$sName = $oEventData->Get('name');
|
||||
$this->Debug("Object from $sName: Fire event '$sEvent'");
|
||||
EventService::FireEvent($oEventData);
|
||||
$this->Debug("Object from $sName: End of event '$sEvent'");
|
||||
|
||||
array_shift(self::$aEventList['event']);
|
||||
$oEventData = reset(self::$aEventList['event']);
|
||||
|
||||
if ($oEventData === false) {
|
||||
$bFireEventNow = false;
|
||||
}
|
||||
if ($iCount++ > 10) {
|
||||
throw new Exception('Infinite loop');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestReentranceCRUD extends TestClassesWithDebug
|
||||
{
|
||||
public $sName;
|
||||
public $sEvent1;
|
||||
public $sEvent2;
|
||||
public $bPermanentProtection;
|
||||
|
||||
private static $iIndent1 = 0;
|
||||
private static $iIndent2 = 0;
|
||||
|
||||
public $aSendEvent = [];
|
||||
|
||||
/**
|
||||
* @param $sName
|
||||
* @param $fPriority
|
||||
* @param array $aSendEvent
|
||||
* @param bool $bPermanentProtection
|
||||
*/
|
||||
public function __construct($sName, $fPriority, array $aSendEvent, $bPermanentProtection)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->sEvent1 = EventService::RegisterListener('insert', [$this, 'OnInsert'], null, [], null, $fPriority);
|
||||
$this->sEvent2 = EventService::RegisterListener('update', [$this, 'OnUpdate'], null, [], null, $fPriority);
|
||||
$this->aSendEvent = $aSendEvent;
|
||||
$this->bPermanentProtection = $bPermanentProtection;
|
||||
}
|
||||
|
||||
public function OnInsert(EventData $oData)
|
||||
{
|
||||
$sIndent = str_repeat(' ', self::$iIndent1);
|
||||
$oObject = $oData->Get('object');
|
||||
$sEvent = $oData->GetEvent();
|
||||
$sSource = $oData->GetEventSource();
|
||||
$sName = $sIndent.$this->sName;
|
||||
$this->Debug("$sName: received event '$sEvent' from '$sSource'");
|
||||
EventTest::IncrementCallCount();
|
||||
|
||||
if ($this->aSendEvent['listener1']['update']) {
|
||||
self::$iIndent2 += 1;
|
||||
$this->Debug("$sName: Update");
|
||||
EventService::EnableReentranceProtection($this->sEvent2, $this->bPermanentProtection);
|
||||
$oObject->DBUpdate($sName);
|
||||
EventService::DisableReentranceProtection($this->sEvent2);
|
||||
self::$iIndent2 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function OnUpdate(EventData $oData)
|
||||
{
|
||||
$sIndent = str_repeat(' ', self::$iIndent2);
|
||||
$oObject = $oData->Get('object');
|
||||
$sEvent = $oData->GetEvent();
|
||||
$sSource = $oData->GetEventSource();
|
||||
$sName = $sIndent.$this->sName;
|
||||
$this->Debug("$sName: received event '$sEvent' from '$sSource'");
|
||||
EventTest::IncrementCallCount();
|
||||
if ($this->aSendEvent['listener2']['update']) {
|
||||
self::$iIndent2 += 1;
|
||||
$this->Debug("$sName: Update");
|
||||
EventService::EnableReentranceProtection($this->sEvent2, $this->bPermanentProtection);
|
||||
$oObject->DBUpdate($sName);
|
||||
EventService::DisableReentranceProtection($this->sEvent2);
|
||||
self::$iIndent2 -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestReentrance extends TestClassesWithDebug
|
||||
{
|
||||
public $sName;
|
||||
public $sEvent1;
|
||||
public $sEvent2;
|
||||
public $bPermanentProtection;
|
||||
|
||||
private static $iIndent1 = 0;
|
||||
private static $iIndent2 = 0;
|
||||
|
||||
public $aSendEvent = [];
|
||||
|
||||
/**
|
||||
* @param $sName
|
||||
* @param $fPriority
|
||||
* @param array $aSendEvent
|
||||
* @param bool $bPermanentProtection
|
||||
*/
|
||||
public function __construct($sName, $fPriority, array $aSendEvent, $bPermanentProtection)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->sEvent1 = EventService::RegisterListener('event1', [$this, 'ListenerEvent1'], null, [], null, $fPriority);
|
||||
$this->sEvent2 = EventService::RegisterListener('event2', [$this, 'ListenerEvent2'], null, [], null, $fPriority);
|
||||
$this->aSendEvent = $aSendEvent;
|
||||
$this->bPermanentProtection = $bPermanentProtection;
|
||||
}
|
||||
|
||||
public function ListenerEvent1(EventData $oData)
|
||||
{
|
||||
$sIndent = str_repeat(' ', self::$iIndent1);
|
||||
$sEvent = $oData->GetEvent();
|
||||
$sSource = $oData->GetEventSource();
|
||||
$sName = $sIndent.$this->sName;
|
||||
$this->Debug("$sName: received event '$sEvent' from '$sSource'");
|
||||
EventTest::IncrementCallCount();
|
||||
if ($this->aSendEvent['listener1']['event1']) {
|
||||
$this->Debug("$sName: Fire event 'event1'");
|
||||
self::$iIndent1 += 1;
|
||||
EventService::EnableReentranceProtection($this->sEvent1, $this->bPermanentProtection);
|
||||
EventService::FireEvent(new EventData('event1', $sName));
|
||||
EventService::DisableReentranceProtection($this->sEvent1);
|
||||
self::$iIndent1 -= 1;
|
||||
$this->Debug("$sName: End of event 'event1'");
|
||||
}
|
||||
if ($this->aSendEvent['listener1']['event2']) {
|
||||
$this->Debug("$sName: Fire event 'event2'");
|
||||
self::$iIndent2 += 1;
|
||||
EventService::EnableReentranceProtection($this->sEvent2, $this->bPermanentProtection);
|
||||
EventService::FireEvent(new EventData('event2', $sName));
|
||||
EventService::DisableReentranceProtection($this->sEvent2);
|
||||
self::$iIndent2 -= 1;
|
||||
$this->Debug("$sName: End of event 'event2'");
|
||||
}
|
||||
}
|
||||
|
||||
public function ListenerEvent2(EventData $oData)
|
||||
{
|
||||
$sIndent = str_repeat(' ', self::$iIndent2);
|
||||
$sEvent = $oData->GetEvent();
|
||||
$sSource = $oData->GetEventSource();
|
||||
$sName = $sIndent.$this->sName;
|
||||
$this->Debug("$sName: received event '$sEvent' from '$sSource'");
|
||||
EventTest::IncrementCallCount();
|
||||
if ($this->aSendEvent['listener2']['event1']) {
|
||||
$this->Debug("$sName: Fire event 'event1'");
|
||||
self::$iIndent1 += 1;
|
||||
EventService::EnableReentranceProtection($this->sEvent1, $this->bPermanentProtection);
|
||||
EventService::FireEvent(new EventData('event1', $sName));
|
||||
EventService::DisableReentranceProtection($this->sEvent1);
|
||||
self::$iIndent1 -= 1;
|
||||
$this->Debug("$sName: End of event 'event1'");
|
||||
}
|
||||
if ($this->aSendEvent['listener2']['event2']) {
|
||||
$this->Debug("$sName: Fire event 'event2'");
|
||||
self::$iIndent2 += 1;
|
||||
EventService::EnableReentranceProtection($this->sEvent2, $this->bPermanentProtection);
|
||||
EventService::FireEvent(new EventData('event2', $sName));
|
||||
EventService::DisableReentranceProtection($this->sEvent2);
|
||||
self::$iIndent2 -= 1;
|
||||
$this->Debug("$sName: End of event 'event2'");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user