mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 02:58:43 +02:00
enhance module dependency computation for optimization and admin feedback
This commit is contained in:
@@ -186,7 +186,7 @@ class ModuleDiscovery
|
||||
$sDir = dirname($sFilePath);
|
||||
$aDirs = [
|
||||
$sDir => self::$m_sModulePath,
|
||||
$sDir.'/dictionaries' => self::$m_sModulePath.'/dictionaries'
|
||||
$sDir.'/dictionaries' => self::$m_sModulePath.'/dictionaries',
|
||||
];
|
||||
foreach ($aDirs as $sRootDir => $sPath)
|
||||
{
|
||||
@@ -221,6 +221,13 @@ class ModuleDiscovery
|
||||
return self::OrderModulesByDependencies(self::$m_aModules, $bAbortOnMissingDependency, $aModulesToLoad);
|
||||
}
|
||||
|
||||
public static function SortModulesByCountOfDepencenciesDescending(array &$aOngoingDependencies) : void
|
||||
{
|
||||
uasort($aOngoingDependencies, function (array $aDeps1, array $aDeps2){
|
||||
return count($aDeps1) - count($aDeps2);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Arrange an list of modules, based on their (inter) dependencies
|
||||
* @param array $aModules The list of modules to process: 'id' => $aModuleInfo
|
||||
@@ -233,28 +240,34 @@ class ModuleDiscovery
|
||||
{
|
||||
// Order the modules to take into account their inter-dependencies
|
||||
$aDependencies = [];
|
||||
$aOngoingDependencies = [];
|
||||
$aSelectedModules = [];
|
||||
foreach($aModules as $sId => $aModule)
|
||||
{
|
||||
list($sModuleName, ) = self::GetModuleName($sId);
|
||||
if (is_null($aModulesToLoad) || in_array($sModuleName, $aModulesToLoad))
|
||||
{
|
||||
$aDependencies[$sId] = $aModule['dependencies'];
|
||||
$aCurrentDependencies = $aModule['dependencies'];
|
||||
$aDependencies[$sId] = $aCurrentDependencies;
|
||||
$aOngoingDependencies[$sId] = $aCurrentDependencies;
|
||||
$aSelectedModules[$sModuleName] = true;
|
||||
}
|
||||
}
|
||||
ksort($aDependencies);
|
||||
self::SortModulesByCountOfDepencenciesDescending($aOngoingDependencies);
|
||||
$aOrderedModules = [];
|
||||
$iLoopCount = 1;
|
||||
while(($iLoopCount < count($aModules)) && (count($aDependencies) > 0) )
|
||||
$iModulesCount = count($aModules);
|
||||
while(($iLoopCount < $iModulesCount) && (count($aOngoingDependencies) > 0) )
|
||||
{
|
||||
foreach($aDependencies as $sId => $aRemainingDeps)
|
||||
foreach($aOngoingDependencies as $sId => $aCurrentRemainingDeps)
|
||||
{
|
||||
$aNextDependencies=[];
|
||||
$bDependenciesSolved = true;
|
||||
foreach($aRemainingDeps as $sDepId)
|
||||
foreach($aCurrentRemainingDeps as $sDepId)
|
||||
{
|
||||
if (!self::DependencyIsResolved($sDepId, $aOrderedModules, $aSelectedModules))
|
||||
{
|
||||
$aNextDependencies[]=$sDepId;
|
||||
$bDependenciesSolved = false;
|
||||
}
|
||||
}
|
||||
@@ -262,26 +275,32 @@ class ModuleDiscovery
|
||||
{
|
||||
$aOrderedModules[] = $sId;
|
||||
unset($aDependencies[$sId]);
|
||||
unset($aOngoingDependencies[$sId]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$aOngoingDependencies[$sId]=$aNextDependencies;
|
||||
}
|
||||
$iLoopCount++;
|
||||
self::SortModulesByCountOfDepencenciesDescending($aOngoingDependencies);
|
||||
}
|
||||
if ($bAbortOnMissingDependency && count($aDependencies) > 0)
|
||||
if ($bAbortOnMissingDependency && count($aOngoingDependencies) > 0)
|
||||
{
|
||||
$aModulesInfo = [];
|
||||
$aModuleDeps = [];
|
||||
foreach($aDependencies as $sId => $aDeps)
|
||||
foreach($aOngoingDependencies as $sId => $aCurrentRemainingDeps)
|
||||
{
|
||||
$aModule = $aModules[$sId];
|
||||
$aDepsWithIcons = [];
|
||||
$aDeps=$aDependencies[$sId];
|
||||
foreach($aDeps as $sIndex => $sDepId)
|
||||
{
|
||||
if (self::DependencyIsResolved($sDepId, $aOrderedModules, $aSelectedModules))
|
||||
{
|
||||
$aDepsWithIcons[$sIndex] = '✅ ' . $sDepId;
|
||||
} else
|
||||
if (in_array($sDepId, $aCurrentRemainingDeps))
|
||||
{
|
||||
$aDepsWithIcons[$sIndex] = '❌ ' . $sDepId;
|
||||
} else
|
||||
{
|
||||
$aDepsWithIcons[$sIndex] = '✅ ' . $sDepId;
|
||||
}
|
||||
}
|
||||
$aModuleDeps[] = "{$aModule['label']} (id: $sId) depends on: ".implode(' + ', $aDepsWithIcons);
|
||||
|
||||
110
tests/php-unit-tests/unitary-tests/setup/ModuleDiscoveryTest.php
Normal file
110
tests/php-unit-tests/unitary-tests/setup/ModuleDiscoveryTest.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Setup;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
class ModuleDiscoveryTest extends ItopTestCase
|
||||
{
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('setup/modulediscovery.class.inc.php');
|
||||
}
|
||||
|
||||
public function testSortModulesByCountOfDepencenciesDescending()
|
||||
{
|
||||
$aOngoingDependencies=[];
|
||||
$aExpectedKeys=[];
|
||||
for($i=5; $i>0; $i--){
|
||||
$sKey = "k$i";
|
||||
$aExpectedKeys[]=$sKey;
|
||||
$aDeps=[];
|
||||
for ($j=0; $j<$i; $j++){
|
||||
$aDeps[]=$j;
|
||||
}
|
||||
$aOngoingDependencies[$sKey]=$aDeps;
|
||||
}
|
||||
sort($aExpectedKeys);
|
||||
|
||||
\ModuleDiscovery::SortModulesByCountOfDepencenciesDescending($aOngoingDependencies);
|
||||
|
||||
$this->assertEquals($aExpectedKeys, array_keys($aOngoingDependencies));
|
||||
}
|
||||
|
||||
public function testOrderModulesByDependencies_CheckMissingDependenciesAreCorrectlyOrderedInTheException()
|
||||
{
|
||||
$sExpectedMessage = <<<MSG
|
||||
The following modules have unmet dependencies:
|
||||
label2 (id: id2/456) depends on: ❌ id3/666,
|
||||
label1 (id: id1/123) depends on: ❌ id3/666 + ❌ id4/666
|
||||
MSG;
|
||||
|
||||
$this->expectExceptionMessage($sExpectedMessage);
|
||||
|
||||
$aModules=[
|
||||
"id1/123" => [
|
||||
'dependencies' => [ 'id3/666', 'id4/666'],
|
||||
'label' => 'label1',
|
||||
],
|
||||
"id2/456" => [
|
||||
'dependencies' => ['id3/666'],
|
||||
'label' => 'label2',
|
||||
],
|
||||
];
|
||||
\ModuleDiscovery::OrderModulesByDependencies($aModules, true);
|
||||
}
|
||||
|
||||
public function testOrderModulesByDependencies_ValidateExceptionWithSomeDependenciesResolved()
|
||||
{
|
||||
$sExpectedMessage = <<<MSG
|
||||
The following modules have unmet dependencies:
|
||||
label1 (id: id1/123) depends on: ✅ id2/456 + ❌ id4/666
|
||||
MSG;
|
||||
|
||||
$this->expectExceptionMessage($sExpectedMessage);
|
||||
|
||||
$aModules=[
|
||||
"id1/123" => [
|
||||
'dependencies' => [ 'id2/456', 'id4/666'],
|
||||
'label' => 'label1',
|
||||
],
|
||||
"id2/456" => [
|
||||
'dependencies' => [],
|
||||
'label' => 'label2',
|
||||
],
|
||||
];
|
||||
\ModuleDiscovery::OrderModulesByDependencies($aModules, true);
|
||||
}
|
||||
|
||||
public function testOrderModulesByDependencies_ResolveOk()
|
||||
{
|
||||
|
||||
$aModules=[
|
||||
"id1/1" => [
|
||||
'dependencies' => [ 'id2/2'],
|
||||
'label' => 'label1',
|
||||
],
|
||||
"id2/2" => [
|
||||
'dependencies' => ['id3/3'],
|
||||
'label' => 'label2',
|
||||
],
|
||||
"id3/3" => [
|
||||
'dependencies' => ['id4/4'],
|
||||
'label' => 'label3',
|
||||
],
|
||||
"id4/4" => [
|
||||
'dependencies' => [],
|
||||
'label' => 'label4',
|
||||
],
|
||||
];
|
||||
$aResult = \ModuleDiscovery::OrderModulesByDependencies($aModules, true);
|
||||
|
||||
$aExpected = [
|
||||
"id4/4",
|
||||
"id3/3",
|
||||
"id2/2",
|
||||
"id1/1",
|
||||
];
|
||||
$this->assertEquals($aExpected, array_keys($aResult));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user