module dependency validation moved in a core folder + cleanup dedicated unit/integration tests

This commit is contained in:
odain
2025-09-11 12:09:24 +02:00
parent 64bec2181f
commit e37322e631
5 changed files with 129 additions and 155 deletions

View File

@@ -1,7 +1,5 @@
<?php
namespace Combodo\iTop\Test\UnitTest;
class XmlModule {
public string $sModuleName;
public array $aDependencyModulesNames=[];

View File

@@ -1,7 +1,5 @@
<?php
namespace Combodo\iTop\Test\UnitTest;
class XmlModuleMetaInfo {
public string $sLastNodeId;
public string $sNodeName;

View File

@@ -13,18 +13,9 @@
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Test\UnitTest\Integration;
use ApplicationException;
use Combodo\iTop\Test\UnitTest\XmlModule;
use Combodo\iTop\Test\UnitTest\XmlModuleMetaInfo;
use utils;
require_once __DIR__ . '/XmlModuleMetaInfo.php';
require_once __DIR__ . '/XmlModule.php';
use ModuleInstallerAPI;
/**
* @group modulesDependencyValidation
*/
@@ -44,7 +35,6 @@ class iTopModulesDependencyValidationService {
{
if (!isset(self::$oInstance)) {
self::$oInstance = new iTopModulesDependencyValidationService();
self::ReadModuleMetaInfo();
}
return self::$oInstance;
@@ -54,54 +44,21 @@ class iTopModulesDependencyValidationService {
self::$oInstance = $instance;
}
public static function ReadModuleMetaInfo(): void
{
require_once APPROOT."setup/modulediscovery.class.inc.php";
require_once(APPROOT.'/setup/moduleinstaller.class.inc.php');
private static function GetModulesDataByModuleName() : array {
if (count(self::$aModulesDataByModuleName)>0){
return;
return self::$aModulesDataByModuleName;
}
$aDirsToScan = [
APPROOT.'datamodels/2.x',
APPROOT.'extensions',
APPROOT.'extensions/*',
APPROOT.'extensions',
APPROOT.'data/production-modules',
APPROOT.'data/production-modules',
APPROOT.'data/production-modules/*',
];
$aFilesToRemove=[];
foreach ($aDirsToScan as $sDir){
foreach (glob("$sDir/*/module.*.php") as $sFile) {
$sContent = file_get_contents($sFile);
$sContent=str_replace('SetupWebPage::AddModule', '$aModuleData=array', $sContent);
self::$aModulesDataByModuleName = ModuleDiscovery::GetAvailableModules($aDirsToScan, true);
$sTempFile = tempnam(sys_get_temp_dir(), 'modulefile_');
$aFilesToRemove[]=$sTempFile;
file_put_contents($sTempFile, $sContent);
require_once $sTempFile;
//replace tmp file by real module path
$aModuleData[0]=$sFile;
$sModuleId=$aModuleData[1];
list($sModuleName, $sVersion) = \ModuleDiscovery::GetModuleName($sModuleId);
self::$aModulesDataByModuleName[$sModuleName] = $aModuleData;
}
ksort(self::$aModulesDataByModuleName);
foreach ($aFilesToRemove as $sTmpFile){
@unlink($sTmpFile);
}
}
}
public function GetModuleMetainfo($sModuleId)
{
return self::$aModulesDataByModuleName[$sModuleId] ?? [];
return self::$aModulesDataByModuleName;
}
public function ListDatamodelFiles() : array
@@ -176,10 +133,10 @@ class iTopModulesDependencyValidationService {
public function FetchAllDependenciesViaModulesFiles()
{
$aFullnameClassesByModuleName=[];
foreach (self::$aModulesDataByModuleName as $sModuleName => $aModuleData){
foreach (self::GetModulesDataByModuleName() as $sModuleName => $aModuleData){
//echo "$sModuleName\n";
$aFiles = $aModuleData[2]['datamodel'] ?? [];
$sDir = dirname($aModuleData[0]);
$sDir = dirname($aModuleData['module_file_path']);
$aDeps=[];
foreach ($aFiles as $sFile){
@@ -194,12 +151,12 @@ class iTopModulesDependencyValidationService {
}
foreach ($aFullnameClassesByModuleName as $sModuleName => $aFullnameClasses){
foreach (self::$aModulesDataByModuleName as $sModuleName2 => $aModuleData){
foreach (self::GetModulesDataByModuleName() as $sModuleName2 => $aModuleData){
if ($sModuleName2 === $sModuleName){
continue;
}
$sDir = dirname($aModuleData[0]);
$sDir = dirname($aModuleData['module_file_path']);
if (count($aFullnameClassesByModuleName)==0){
continue;

View File

@@ -0,0 +1,114 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
* This file is part of iTop.
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Test\UnitTest\Integration;
use ApplicationException;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use iTopModulesDependencyValidationService;
use XmlModule;
use XmlModuleMetaInfo;
use utils;
use ModuleInstallerAPI;
/**
* @package Combodo\iTop\Test\UnitTest\Setup
*/
class iTopModulesDependencyValidationServiceTest extends ItopDataTestCase {
private iTopModulesDependencyValidationService $oiTopModulesDependencyValidationService;
private array $aFilesToRemove = [];
protected function setUp(): void
{
parent::setUp();
$this->RequireOnceItopFile('setup/modulediscovery.class.inc.php');
$this->RequireOnceItopFile('setup/module/iTopModulesDependencyValidationService.php');
}
protected function tearDown(): void
{
parent::tearDown(); // TODO: Change the autogenerated stub
foreach ($this->aFilesToRemove as $sTmpFile){
@unlink($sTmpFile);
}
iTopModulesDependencyValidationService::SetInstance(null);
}
/**
* Module dependency validation: make sure dependencies are correct toward classes/interfaces coming from PHP/Xml datamodel files
*/
public function testReadModuleFileData()
{
iTopModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaModulesFiles();
$this->testModulesBasedOnDMFilesOnly();
}
/**
* Module dependency validation: make sure dependencies are correct toward classes/interfaces coming from Xml datamodel files
*/
public function testModulesBasedOnDMFilesOnly()
{
iTopModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaDM();
$aErrors=[];
/** @var XmlModule $oXmlModule */
foreach (iTopModulesDependencyValidationService::GetInstance()->aModules as $sModuleName => $oXmlModule) {
$aCurrentDeps = iTopModulesDependencyValidationService::GetInstance()::$aModulesDataByModuleName[$sModuleName][2]['dependencies'] ?? [];
$aModuleErrors=[];
foreach ($oXmlModule->aDependencyModulesNames as $sDepModuleName => $oXmlModule2){
$sXmlUIDs = implode('|', $oXmlModule->aXMlMetaInfosByModuleNames[$sDepModuleName]);
$bResolved=false;
foreach ($aCurrentDeps as $sDepString){
$oModuleDependency = new \iTopCoreModuleDependency($sDepString);
if (in_array($sDepModuleName, $oModuleDependency->GetPotentialPrerequisiteModuleNames())) {
$bResolved=true;
break;
}
foreach ($oModuleDependency->GetPotentialPrerequisiteModuleNames() as $sPotentialDepModuleName){
/** @var XmlModule $oXmlModule2 */
$oXmlModule2 = iTopModulesDependencyValidationService::GetInstance()->aModules[$sPotentialDepModuleName]??null;
if (! is_null($oXmlModule2) && $oXmlModule2->Depends($sDepModuleName)){
$bResolved=true;
break;
}
}
if ($bResolved) {
break;
}
}
if (! $bResolved){
$aModuleErrors []= "$sModuleName depends on $sDepModuleName but missing in module dependencies: " . implode(' & ', $aCurrentDeps) . ". ($sXmlUIDs)";
}
}
if (count($aModuleErrors)){
$aErrors[$sModuleName]=$aModuleErrors;
}
}
$this->assertEquals(0, count($aErrors), var_export($aErrors, true));
}
}

View File

@@ -13,30 +13,23 @@
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Test\UnitTest\Integration;
namespace Combodo\iTop\Test\UnitTest\Setup\Module;
use ApplicationException;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use Combodo\iTop\Test\UnitTest\XmlModule;
use Combodo\iTop\Test\UnitTest\XmlModuleMetaInfo;
use utils;
use iTopModulesDependencyValidationService;
require_once __DIR__ . '/XmlModuleMetaInfo.php';
require_once __DIR__ . '/XmlModule.php';
require_once __DIR__ . '/iTopModulesDependencyValidationService.php';
use ModuleInstallerAPI;
/**
* @package Combodo\iTop\Test\UnitTest\Setup
*/
class iTopModulesDependencyTest extends ItopTestCase {
class iTopModulesDependencyValidationServiceTest extends ItopTestCase {
private iTopModulesDependencyValidationService $oiTopModulesDependencyValidationService;
private array $aFilesToRemove = [];
protected function setUp(): void
{
parent::setUp(); // TODO: Change the autogenerated stub
parent::setUp();
$this->RequireOnceItopFile('setup/module/iTopModulesDependencyValidationService.php');
}
protected function tearDown(): void
@@ -48,67 +41,6 @@ class iTopModulesDependencyTest extends ItopTestCase {
iTopModulesDependencyValidationService::SetInstance(null);
}
/**
* Module dependency validation: make sure dependencies are correct toward classes/interfaces coming from PHP/Xml datamodel files
*/
public function testReadModuleFileData()
{
iTopModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaModulesFiles();
$this->testModulesBasedOnDMFilesOnly();
}
/**
* Module dependency validation: make sure dependencies are correct toward classes/interfaces coming from Xml datamodel files
*/
public function testModulesBasedOnDMFilesOnly()
{
iTopModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaDM();
$aErrors=[];
/** @var XmlModule $oXmlModule */
foreach (iTopModulesDependencyValidationService::GetInstance()->aModules as $sModuleName => $oXmlModule) {
$aCurrentDeps = iTopModulesDependencyValidationService::GetInstance()::$aModulesDataByModuleName[$sModuleName][2]['dependencies'] ?? [];
$aModuleErrors=[];
foreach ($oXmlModule->aDependencyModulesNames as $sDepModuleName => $oXmlModule2){
$sXmlUIDs = implode('|', $oXmlModule->aXMlMetaInfosByModuleNames[$sDepModuleName]);
$bResolved=false;
foreach ($aCurrentDeps as $sDepString){
$oModuleDependency = new \iTopCoreModuleDependency($sDepString);
if (in_array($sDepModuleName, $oModuleDependency->GetPotentialPrerequisiteModuleNames())) {
$bResolved=true;
break;
}
foreach ($oModuleDependency->GetPotentialPrerequisiteModuleNames() as $sPotentialDepModuleName){
/** @var XmlModule $oXmlModule2 */
$oXmlModule2 = iTopModulesDependencyValidationService::GetInstance()->aModules[$sPotentialDepModuleName]??null;
if (! is_null($oXmlModule2) && $oXmlModule2->Depends($sDepModuleName)){
$bResolved=true;
break;
}
}
if ($bResolved) {
break;
}
}
if (! $bResolved){
$aModuleErrors []= "$sModuleName depends on $sDepModuleName but missing in module dependencies: " . implode(' & ', $aCurrentDeps) . ". ($sXmlUIDs)";
}
}
if (count($aModuleErrors)){
$aErrors[$sModuleName]=$aModuleErrors;
}
}
$this->assertEquals(0, count($aErrors), var_export($aErrors, true));
}
public function testListDeclaredFullnameClassesFromPhpFile()
{
$aExpected = [
@@ -146,31 +78,6 @@ class iTopModulesDependencyTest extends ItopTestCase {
$this->assertEquals($aExpected, iTopModulesDependencyValidationService::GetInstance()->ListDeclaredFullnameClassesFromPhpFile(APPROOT . 'datamodels/2.x/itop-oauth-client/vendor/autoload.php'));
}
public function testReadModuleMetaInfo()
{
$this->markTestSkipped();
$aExpected = [
'/var/www/html/iTop/datamodels/2.x/itop-portal-base/module.itop-portal-base.php',
'itop-portal-base/3.2.1',
[
'label' => 'Portal Development Library',
'category' => 'Portal',
'dependencies' => [ 'itop-attachments/3.2.1' ],
'mandatory' => true,
'visible' => false,
'datamodel' => [ 'portal/vendor/autoload.php' ],
'webservice' => [],
'dictionary' => [],
'data.struct' => [],
'data.sample' => [],
'doc.manual_setup' => '',
'doc.more_information' => '',
'settings' => [],
],
];
$this->assertEquals($aExpected, iTopModulesDependencyValidationService::GetInstance()->GetModuleMetainfo('itop-portal-base'));
}
public function testGetFirstFoundDepsUID() {
$sOutput=<<<TXT
/var/www/html/Professional-3.2.1-16428/web/datamodels/2.x/authent-token/src/Hook/MyAccountSectionTabContentExtension.php:Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension