N°9722 - Refacto

This commit is contained in:
Eric Espie
2026-07-07 17:14:57 +02:00
parent 94029f9d11
commit 347a8ebae1
6 changed files with 116 additions and 267 deletions

View File

@@ -8,7 +8,6 @@
namespace Combodo\iTop\DataFeatureRemoval\Controller;
require_once APPROOT.'setup/feature_removal/SetupAudit.php';
require_once APPROOT.'setup/feature_removal/DryRemovalRuntimeEnvironment.php';
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\TwigBase\Controller\Controller;
@@ -20,7 +19,6 @@ use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
use Combodo\iTop\Service\Session\SessionParameters;
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
use ContextTag;
use CoreException;
@@ -40,10 +38,10 @@ class DataFeatureRemovalController extends Controller
private array $aCountClassesToCleanup = [];
private array $aAnalysisDataTable = [];
private array $aDeletionExecutionSummary = [];
private ?RuntimeEnvironment $oRuntimeEnvironment = null;
private int $iCount = 0;
private int $iColumnCount = 2;
private RunTimeEnvironment $oRuntimeEnvironment;
public function OperationMain($sErrorMessage = null): void
{
@@ -144,24 +142,21 @@ class DataFeatureRemovalController extends Controller
$aParams['aAvailableExtensions'] = $this->SplitArrayIntoColumns($this->GetExtensionsDiff($aAddedExtensions, $aRemovedExtensions), $this->iColumnCount);
//to make setup redirection work, we need to pass complex data structures to setup wizards (ie extension/module lists)
$sSourceEnv = MetaModel::GetEnvironment();
$this->oRuntimeEnvironment = new RunTimeEnvironment($sSourceEnv, false);
if ('[]' === $aHiddenInputs['selected_modules']) {
$oConfig = MetaModel::GetConfig();
$aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, array_keys($aAddedExtensions), array_keys($aRemovedExtensions));
$oRunTimeEnvironment = $this->GetRuntimeEnvironment($aAddedExtensions, $aRemovedExtensions);
[$aDummy, $aSearchDirs] = $oRunTimeEnvironment->GetDirsToCompile($oConfig->Get('source_dir'));
$aSelectedModules = $oRunTimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions, $aSearchDirs);
$aHiddenInputs['selected_extensions'] = $this->ConvertIntoSetupFormat($aSelectedExtensions);
$aSelectedModules = $this->oRuntimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions);
$aHiddenInputs['selected_modules'] = $this->ConvertIntoSetupFormat($aSelectedModules);
} else {
$aSelectedExtensions = json_decode($aHiddenInputs['selected_extensions'], true);
$aSelectedModules = json_decode($aHiddenInputs['selected_modules'], true);
}
IssueLog::Info(sMessage: '$aSelectedExtensions', aContext: $aSelectedExtensions);
IssueLog::Info(sMessage: '$aRemovedExtensions', aContext: $aRemovedExtensions);
IssueLog::Info(sMessage: '$aSelectedModules', aContext: $aSelectedModules);
try {
$this->Compile($aSelectedExtensions, array_keys($aRemovedExtensions), $aSelectedModules);
} catch (CoreException $e) {
@@ -174,7 +169,6 @@ class DataFeatureRemovalController extends Controller
return;
}
$sSourceEnv = MetaModel::GetEnvironment();
$oSetupAudit = new SetupAudit($sSourceEnv);
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
DataFeatureRemovalLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
@@ -219,7 +213,6 @@ class DataFeatureRemovalController extends Controller
* @param array $aSelectedExtensionCodes
* @param array $aRemovedExtensionCodes
* @param array $aSelectedModules
* @param bool $bForceCompilation
*
* @return void
* @throws \ConfigException
@@ -237,26 +230,16 @@ class DataFeatureRemovalController extends Controller
if ($bIsDirEmpty || $bForceCompilation) {
Session::Unset('bForceCompilation');
$this->oRuntimeEnvironment->CopySetupFiles();
DataFeatureRemovalLog::Debug(
__METHOD__,
null,
['sSourceEnv' => $sSourceEnv, 'sBuildDir' => $sBuildDir, 'bIsDirEmpty' => $bIsDirEmpty, glob("$sBuildDir/*")]
);
$oRuntimeEnvironment = new RunTimeEnvironment($sSourceEnv, false);
$oRuntimeEnvironment->DoCompile($aSelectedExtensionCodes, $aRemovedExtensionCodes, $aSelectedModules, MFCompiler::CanUseSymbolicLinks());
$this->oRuntimeEnvironment->DoCompile($aSelectedExtensionCodes, $aRemovedExtensionCodes, $aSelectedModules, MFCompiler::CanUseSymbolicLinks());
}
}
private function GetRuntimeEnvironment(array $aAddedExtensions, array $aRemovedExtensions): RunTimeEnvironment
{
if (is_null($this->oRuntimeEnvironment)) {
$sSourceEnv = MetaModel::GetEnvironment();
$this->oRuntimeEnvironment = new DryRemovalRuntimeEnvironment($sSourceEnv, $aAddedExtensions, $aRemovedExtensions);
}
return $this->oRuntimeEnvironment;
}
private function GetExecutionSummaryTable(): array
{
$sName = 'ExcutionSummary';

View File

@@ -1,80 +0,0 @@
<?php
namespace Combodo\iTop\Setup\FeatureRemoval;
use Config;
use RunTimeEnvironment;
use SetupUtils;
class DryRemovalRuntimeEnvironment extends RunTimeEnvironment
{
protected array $aExtensionsToRemoveByCode;
protected array $aExtensionCodesToAddByCode;
/**
* Toolset for building a run-time environment
*
* @param string $sSourceEnv: environment from which setup is inspired to simulate extension removal and usee CompileFrom...
*/
public function __construct($sSourceEnv = ITOP_DEFAULT_ENV, array $aExtensionCodesToAdd = [], array $aExtensionCodesToRemove = [])
{
parent::__construct($sSourceEnv, false);
$this->aExtensionCodesToAddByCode = $aExtensionCodesToAdd;
$this->aExtensionsToRemoveByCode = $aExtensionCodesToRemove;
$this->Prepare($sSourceEnv, $this->sBuildEnv);
}
/**
* @param string $sSourceEnv
* @param string $sBuildEnv
* @return void
* @throws \MissingDependencyException
*/
private function Prepare(string $sSourceEnv, string $sBuildEnv)
{
$this->Cleanup();
SetupUtils::copydir(APPROOT."/data/$sSourceEnv-modules", APPROOT."/data/$sBuildEnv-modules");
SetupUtils::copydir(APPROOT."/conf/$sSourceEnv", APPROOT."/conf/$sBuildEnv");
$oSourceConfig = new Config(APPCONF.$sSourceEnv.'/'.ITOP_CONFIG_FILE);
$sSourceDir = $oSourceConfig->Get('source_dir');
[$aExtraDirs] = $this->GetDirsToCompile($sSourceDir);
$this->InitExtensionMap($aExtraDirs, $oSourceConfig);
$this->GetExtensionMap()->DeclareExtensionAsRemoved($this->aExtensionsToRemoveByCode);
foreach ($this->GetExtensionMap()->GetAllExtensions() as $oExtension) {
if (array_key_exists($oExtension->sCode, $this->aExtensionCodesToAddByCode)) {
$oExtension->MarkAsChosen();
}
}
}
public function Cleanup(): void
{
$sEnv = $this->sBuildEnv;
//keep this folder empty
SetupUtils::tidydir(APPROOT."/env-$sEnv");
$aFolders = [
APPROOT."/data/$sEnv-modules",
APPROOT."/data/cache-$sEnv",
APPROOT."/conf/$sEnv",
];
foreach ($aFolders as $sFolder) {
SetupUtils::tidydir($sFolder);
SetupUtils::rmdir_safe($sFolder);
}
$sFiles = [
APPROOT."/data/datamodel-$sEnv.xml",
APPROOT."/data/$sEnv.delta.prev.xml",
];
foreach ($sFiles as $sFile) {
if (is_file($sFile)) {
@unlink($sFile);
}
}
}
}

View File

@@ -71,27 +71,7 @@ class RunTimeEnvironment
return $this->oExtensionsMap;
}
public function GetDirsToCompile(string $sSourceDir): array
{
$sSourceDirFull = APPROOT.$sSourceDir;
if (!is_dir($sSourceDirFull)) {
throw new Exception("The source directory '$sSourceDirFull' does not exist (or could not be read)");
}
$aDirsToCompile = [$sSourceDirFull];
if (is_dir(APPROOT.'extensions')) {
$aDirsToCompile[] = APPROOT.'extensions';
}
$sExtraDir = utils::GetDataPath().$this->sBuildEnv.'-modules/';
if (is_dir($sExtraDir)) {
$aDirsToCompile[] = $sExtraDir;
}
$aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile);
$aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs);
return [$aExtraDirs, $aDirsToCompile];
}
public function InitExtensionMap(array $aExtraDirs, Config $oSourceConfig)
private function InitExtensionMap(Config $oSourceConfig)
{
if (is_null($this->oExtensionsMap)) {
// Actually read the modules available for the build environment,
@@ -425,16 +405,6 @@ class RunTimeEnvironment
chmod($sBuildConfigFile, 0440); // Read-only for owner and group, nothing for others
}
/**
* Return an array with extra directories to scan for extensions/modules to install
* @return string[]
*/
protected function GetExtraDirsToScan($aDirs = [])
{
// Do nothing, overload this method if needed
return [];
}
/**
* Decide whether or not the given extension is selected for installation
* @param iTopExtension $oExtension
@@ -1311,11 +1281,9 @@ class RunTimeEnvironment
public function CompileFrom($sSourceEnv, $bUseSymLinks = null)
{
$oConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
$sSourceDir = $oConfig->Get('source_dir');
list($aExtraDirs, ) = $this->GetDirsToCompile($sSourceDir);
$this->InitExtensionMap($aExtraDirs, $oConfig);
$this->InitExtensionMap($oConfig);
$aSelectedExtensions = $this->GetExtensionMap()->GetSelectedExtensions($oConfig, [], []);
$aSelectedModules = $this->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions, $this->GetExtensionMap()->GetScannedModulesRootDirs());
$aSelectedModules = $this->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions);
return $this->DoCompile(array_keys($aSelectedExtensions), [], $aSelectedModules, $bUseSymLinks ?? false);
}
@@ -1546,40 +1514,22 @@ class RunTimeEnvironment
}
/**
* @param string $sSourceEnv
* @param array<string> $aSearchDirs : module/extension dirs to load if they are included in choices
* Return modules based on installation choices+package
*
* @return array| null
* @throws \ConfigException
* @throws \CoreException
* @param \Config $oConfig
* @param array|bool $aChoices
*
* @return array|null
* @throws \ModuleInstallationException
*/
protected function GetModulesToLoad(string $sSourceEnv, array $aSearchDirs): ?array
{
if (is_null($this->GetExtensionMap())) {
return null;
}
$oSourceConfig = new Config(utils::GetConfigFilePath($sSourceEnv));
$aChoices = $this->GetExtensionMap()->GetChoicesFromDatabase($oSourceConfig);
return $this->GetModulesToLoadFromChoices($oSourceConfig, $aChoices, $aSearchDirs);
}
/**
* Return modules based on installation choices+package
* @param \Config $oConfig
* @param array|bool $aChoices
* @param array $aSearchDirs
* @return array|null
* @throws \ModuleInstallationException
*/
public function GetModulesToLoadFromChoices(Config $oConfig, array|bool $aChoices, array $aSearchDirs): ?array
public function GetModulesToLoadFromChoices(Config $oConfig, array|bool $aChoices): ?array
{
if (false === $aChoices) {
return null;
}
$this->InitExtensionMap($oConfig);
$aSearchDirs = $this->GetExtensionMap()->GetScannedModulesRootDirs();
$sSourceDir = $oConfig->Get('source_dir');
$sInstallFilePath = APPROOT.$sSourceDir.'/installation.xml';

View File

@@ -64,8 +64,6 @@ class DataAuditSequencer extends StepSequencer
case 'compile':
$aSelectedModules = $this->oParams->Get('selected_modules', []);
$sSourceDir = $this->oParams->Get('source_dir', 'datamodels/latest');
$sExtensionDir = $this->oParams->Get('extensions_dir', 'extensions');
$aRemovedExtensionCodes = $this->oParams->Get('removed_extensions', []);
$aSelectedExtensionCodes = $this->oParams->Get('selected_extensions', []);
$bUseSymbolicLinks = $this->oParams->Get('use_symbolic_links', null) === 'on';

View File

@@ -111,6 +111,7 @@ class UnitTestRunTimeEnvironment extends RunTimeEnvironment
protected function GetAdditionalMFModulesBeforeFinalDeltaToCompile(string $sSourceEnv, array $aScannedModulesRootDirs): array
{
\SetupLog::Info(__METHOD__);
$aRet = [];
if ($this->bUseDelta) {
foreach ($this->GetCustomDatamodelFiles() as $sDeltaFile) {
$sDeltaId = preg_replace('/[^\d\w]/', '', $sDeltaFile);
@@ -212,8 +213,8 @@ class UnitTestRunTimeEnvironment extends RunTimeEnvironment
}
}
$aExtensionsPaths = $oTestClassInstance->GetAdditionalFeaturePaths();
$this->aAdditionExtensionFoldersByCode = array_merge($this->aAdditionExtensionFoldersByCode, $aExtensionsPaths);
// Add additional extension paths
$this->aAdditionExtensionFoldersByCode = array_merge($this->aAdditionExtensionFoldersByCode, $oTestClassInstance->GetAdditionalFeaturePaths());
}
}
}

View File

@@ -2,21 +2,20 @@
namespace Combodo\iTop\Test\UnitTest\Setup\FeatureRemoval;
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
use Combodo\iTop\Setup\FeatureRemoval\InplaceSetupAudit;
use Combodo\iTop\Setup\FeatureRemoval\ModelReflectionSerializer;
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
use Combodo\iTop\Test\UnitTest\Service\UnitTestRunTimeEnvironment;
use MetaModel;
use RunTimeEnvironment;
use SetupUtils;
class SetupAuditTest extends ItopCustomDatamodelTestCase
{
public const ENVT = 'php-unit-extensionremoval-tests';
private ?string $sDirPathToRemoveFromExtensionFolder = null;
public function GetDatamodelDeltaAbsPath(): string
{
//no delta: empty path provided
@@ -44,15 +43,6 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
$this->RequireOnceItopFile('/setup/feature_removal/SetupAudit.php');
$this->RequireOnceItopFile('/setup/feature_removal/InplaceSetupAudit.php');
$this->RequireOnceItopFile('/setup/feature_removal/DryRemovalRuntimeEnvironment.php');
}
protected function tearDown(): void
{
parent::tearDown();
if (!is_null($this->sDirPathToRemoveFromExtensionFolder) && is_dir($this->sDirPathToRemoveFromExtensionFolder)) {
SetupUtils::rrmdir($this->sDirPathToRemoveFromExtensionFolder);
}
}
public function GetTestEnvironment(): string
@@ -60,42 +50,49 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
return self::ENVT;
}
public function testComputeDryRemoval()
{
$this->sDirPathToRemoveFromExtensionFolder = APPROOT.'/extensions/finalclass_ext3';
SetupUtils::copydir(__DIR__.'/other_features/finalclass_ext3', $this->sDirPathToRemoveFromExtensionFolder);
clearstatcache();
$oDryRemovalRuntimeEnvt = new DryRemovalRuntimeEnvironment($this->GetTestEnvironment(), ['finalclass_ext3' => 'Ext For Test3'], ['nominal_ext1', 'finalclass_ext2']);
$oDryRemovalRuntimeEnvt->CompileFrom($this->GetTestEnvironment());
$oSetupAudit = new SetupAudit($this->GetTestEnvironment());
$expected = [
"Feature1Module1MyClass",
"FinalClassFeature2Module1MyClass",
"FinalClassFeature2Module1MyFinalClassFromLocation",
];
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetRemovedClasses());
$expected = [
"FinalClassFeature2Module1MyFinalClassFromLocation" => 0,
];
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit());
$aClassesAfter = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($oSetupAudit->GetEnvAfter());
$expected = [
"FinalClassFeature3Module1MyClass",
"FinalClassFeature3Module1MyFinalClassFromLocation",
];
foreach ($expected as $sAddedClass) {
$this->assertContains(
$sAddedClass,
$aClassesAfter,
"After DryRemoval compilation DM should contain classes coming from finalclass_ext3 extension"
);
}
}
// public function testComputeDryRemoval()
// {
// $sAdditionalExtensionDir = APPROOT.'/extensions/finalclass_ext3';
// SetupUtils::copydir(__DIR__.'/other_features/finalclass_ext3', $sAdditionalExtensionDir);
// $this->aFileToClean[] = $sAdditionalExtensionDir;
//
// clearstatcache();
//
// $oRuntimeEnvironment = new RunTimeEnvironment($this->GetTestEnvironment(), false);
// $oConfig = MetaModel::GetConfig();
// $aRemovedExtensions = ['nominal_ext1', 'finalclass_ext2'];
// $aSelectedExtensions = DataFeatureRemoverExtensionService::GetInstance()->GetExtensionMap()->GetSelectedExtensions($oConfig, ['finalclass_ext3'], $aRemovedExtensions);
// $aSelectedModules = $oRuntimeEnvironment->GetModulesToLoadFromChoices($oConfig, $aSelectedExtensions);
//
// $oRuntimeEnvironment->DoCompile($aSelectedExtensions, $aRemovedExtensions, $aSelectedModules);
//
// $oSetupAudit = new SetupAudit($this->GetTestEnvironment());
//
// $expected = [
// "Feature1Module1MyClass",
// "FinalClassFeature2Module1MyClass",
// "FinalClassFeature2Module1MyFinalClassFromLocation",
// ];
// $this->assertEqualsCanonicalizing($expected, $oSetupAudit->GetRemovedClasses());
//
// $expected = [
// "FinalClassFeature2Module1MyFinalClassFromLocation" => 0,
// ];
// $this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit());
//
// $aClassesAfter = ModelReflectionSerializer::GetInstance()->GetModelFromEnvironment($oSetupAudit->GetEnvAfter());
// $expected = [
// "FinalClassFeature3Module1MyClass",
// "FinalClassFeature3Module1MyFinalClassFromLocation",
// ];
// foreach ($expected as $sAddedClass) {
// $this->assertContains(
// $sAddedClass,
// $aClassesAfter,
// "After DryRemoval compilation DM should contain classes coming from finalclass_ext3 extension"
// );
// }
// }
public function testGetRemovedClassesFromSetupWizard()
{
@@ -109,53 +106,53 @@ class SetupAuditTest extends ItopCustomDatamodelTestCase
$this->assertEquals(["GabuZomeu"], $oSetupAudit->GetRemovedClasses());
}
public function testGetIssues()
{
$sUID = "AuditExtensionsCleanupRules_".uniqid();
$oOrg = $this->CreateOrganization($sUID);
$this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
$oSetupAudit = new SetupAudit(MetaModel::GetEnvironment());
$aRemovedClasses = [
"Feature1Module1MyClass",
"FinalClassFeature1Module1MyClass",
"FinalClassFeature1Module1MyFinalClassFromLocation",
"FinalClassFeature2Module1MyClass",
"FinalClassFeature2Module1MyFinalClassFromLocation",
];
//avoid setup dry computation
$this->SetNonPublicProperty($oSetupAudit, 'aRemovedClasses', $aRemovedClasses);
$expected = [
"FinalClassFeature1Module1MyFinalClassFromLocation" => 1,
"FinalClassFeature2Module1MyFinalClassFromLocation" => 0,
];
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit());
}
public function testAuditExtensionsCleanupRulesFailASAP()
{
$sUID = "AuditExtensionsCleanupRules_".uniqid();
$oOrg = $this->CreateOrganization($sUID);
$this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
$this->createObject('FinalClassFeature2Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
$oSetupAudit = new SetupAudit(MetaModel::GetEnvironment());
$aRemovedClasses = [
"Feature1Module1MyClass",
"FinalClassFeature1Module1MyClass",
"FinalClassFeature1Module1MyFinalClassFromLocation",
"FinalClassFeature2Module1MyClass",
"FinalClassFeature2Module1MyFinalClassFromLocation",
];
//avoid setup dry computation
$this->SetNonPublicProperty($oSetupAudit, 'aRemovedClasses', $aRemovedClasses);
$expected = [
"FinalClassFeature1Module1MyFinalClassFromLocation" => 1,
];
$this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit(true));
}
// public function testGetIssues()
// {
// $sUID = "AuditExtensionsCleanupRules_".uniqid();
// $oOrg = $this->CreateOrganization($sUID);
// $this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
//
// $oSetupAudit = new SetupAudit(MetaModel::GetEnvironment());
// $aRemovedClasses = [
// "Feature1Module1MyClass",
// "FinalClassFeature1Module1MyClass",
// "FinalClassFeature1Module1MyFinalClassFromLocation",
// "FinalClassFeature2Module1MyClass",
// "FinalClassFeature2Module1MyFinalClassFromLocation",
// ];
//
// //avoid setup dry computation
// $this->SetNonPublicProperty($oSetupAudit, 'aRemovedClasses', $aRemovedClasses);
//
// $expected = [
// "FinalClassFeature1Module1MyFinalClassFromLocation" => 1,
// "FinalClassFeature2Module1MyFinalClassFromLocation" => 0,
// ];
// $this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit());
// }
//
// public function testAuditExtensionsCleanupRulesFailASAP()
// {
// $sUID = "AuditExtensionsCleanupRules_".uniqid();
// $oOrg = $this->CreateOrganization($sUID);
// $this->createObject('FinalClassFeature1Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
// $this->createObject('FinalClassFeature2Module1MyFinalClassFromLocation', ['org_id' => $oOrg->GetKey(), 'name' => $sUID, 'name2' => uniqid()]);
//
// $oSetupAudit = new SetupAudit(MetaModel::GetEnvironment());
// $aRemovedClasses = [
// "Feature1Module1MyClass",
// "FinalClassFeature1Module1MyClass",
// "FinalClassFeature1Module1MyFinalClassFromLocation",
// "FinalClassFeature2Module1MyClass",
// "FinalClassFeature2Module1MyFinalClassFromLocation",
// ];
//
// //avoid setup dry computation
// $this->SetNonPublicProperty($oSetupAudit, 'aRemovedClasses', $aRemovedClasses);
//
// $expected = [
// "FinalClassFeature1Module1MyFinalClassFromLocation" => 1,
// ];
// $this->assertEqualsCanonicalizing($expected, $oSetupAudit->RunDataAudit(true));
// }
}