[WIP][POC] the PhpUnit tests can now build and use various environments

drawbacks: the application startup was emulated since it is not compatible with a custom env. when launched with the CLI (it rely on the session in an incompatible way)
This commit is contained in:
bruno-ds
2021-03-26 12:24:40 +01:00
parent 91fc2d2e2b
commit 80cb3a11db
29 changed files with 4028 additions and 2798 deletions

View File

@@ -0,0 +1,815 @@
<?php
// Copyright (c) 2010-2017 Combodo SARL
//
// 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
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
namespace Combodo\iTop\Test\UnitTest;
/**
* Created by PhpStorm.
* User: Eric
* Date: 20/11/2017
* Time: 11:21
*/
use ArchivedObjectException;
use CMDBSource;
use Contact;
use DBObject;
use DBObjectSet;
use DBSearch;
use Exception;
use Farm;
use FunctionalCI;
use Hypervisor;
use lnkContactToFunctionalCI;
use lnkContactToTicket;
use lnkFunctionalCIToTicket;
use MetaModel;
use Person;
use Server;
use TagSetFieldData;
use Ticket;
use URP_UserProfile;
use VirtualHost;
use VirtualMachine;
/** @see \Combodo\iTop\Test\UnitTest\ItopDataTestCase::CreateObjectWithTagSet() */
define('TAG_CLASS', 'FAQ');
define('TAG_ATTCODE', 'domains');
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class ItopDataTestCase extends ItopTestCase
{
private $iTestOrgId;
// For cleanup
private $aCreatedObjects = array();
const USE_TRANSACTION = true;
const CREATE_TEST_ORG = false;
/**
* @throws Exception
*/
protected function setUp()
{
parent::setUp();
$this->EmulateApplicationStartup();
require_once(APPROOT.'application/utils.inc.php');
if (static::USE_TRANSACTION)
{
CMDBSource::Query('START TRANSACTION');
}
if (static::CREATE_TEST_ORG)
{
$this->CreateTestOrganization();
}
}
/**
* At the time of the writing, je startup process isn't compatible with a custom env declared within the CLI
* @throws \CoreException
* @throws \DictExceptionUnknownLanguage
* @throws \MySQLException
*/
public function EmulateApplicationStartup()
{
// require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
if (function_exists('opcache_reset')) {
// Zend opcode cache
opcache_reset();
}
if (function_exists('apc_clear_cache')) {
// APC(u) cache
apc_clear_cache();
}
if (isset($_SESSION['itop_env'])) {
$sEnv = $_SESSION['itop_env'];
} else {
$sEnv = ITOP_DEFAULT_ENV;
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true, false /* $bTraceSourceFiles */, $sEnv);
}
/**
* @throws Exception
*/
protected function tearDown()
{
if (static::USE_TRANSACTION)
{
$this->debug("ROLLBACK !!!");
CMDBSource::Query('ROLLBACK');
}
else
{
$this->debug("");
$this->aCreatedObjects = array_reverse($this->aCreatedObjects);
foreach ($this->aCreatedObjects as $oObject)
{
/** @var DBObject $oObject */
try
{
$sClass = get_class($oObject);
$iKey = $oObject->GetKey();
$this->debug("Removing $sClass::$iKey");
$oObject->DBDelete();
}
catch (Exception $e)
{
$this->debug($e->getMessage());
}
}
}
}
/**
* @return mixed
*/
public function getTestOrgId()
{
return $this->iTestOrgId;
}
/////////////////////////////////////////////////////////////////////////////
/// Database Utilities
/////////////////////////////////////////////////////////////////////////////
/**
* @param string $sClass
* @param array $aParams
*
* @return DBObject
* @throws Exception
*/
protected function createObject($sClass, $aParams)
{
$oMyObj = MetaModel::NewObject($sClass);
foreach ($aParams as $sAttCode => $oValue)
{
$oMyObj->Set($sAttCode, $oValue);
}
$oMyObj->DBInsert();
$iKey = $oMyObj->GetKey();
$this->debug("Created $sClass::$iKey");
$this->aCreatedObjects[] = $oMyObj;
return $oMyObj;
}
/**
* @param string $sClass
* @param $iKey
* @param array $aParams
*
* @return DBObject
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
*/
protected static function updateObject($sClass, $iKey, $aParams)
{
$oMyObj = MetaModel::GetObject($sClass, $iKey);
foreach ($aParams as $sAttCode => $oValue)
{
$oMyObj->Set($sAttCode, $oValue);
}
$oMyObj->DBUpdate();
return $oMyObj;
}
/**
* Create an Organization in database
*
* @param string $sName
*
* @return \Organization
* @throws Exception
*/
protected function CreateOrganization($sName)
{
/** @var \Organization $oObj */
$oObj = $this->createObject('Organization', array(
'name' => $sName,
));
$this->debug("Created Organization {$oObj->Get('name')}");
return $oObj;
}
/**
* Create a Ticket in database
*
* @param int $iNum
*
* @return Ticket
* @throws Exception
*/
protected function CreateTicket($iNum)
{
/** @var Ticket $oTicket */
$oTicket = $this->createObject('UserRequest', array(
'ref' => 'Ticket_'.$iNum,
'title' => 'TICKET_'.$iNum,
//'request_type' => 'incident',
'description' => 'Created for unit tests.',
'org_id' => $this->getTestOrgId(),
));
$this->debug("Created {$oTicket->Get('title')} ({$oTicket->Get('ref')})");
return $oTicket;
}
protected function RemoveTicket($iNum)
{
$this->RemoveObjects('UserRequest', "SELECT UserRequest WHERE ref = 'Ticket_$iNum'");
}
/**
* Create a Ticket in database
*
* @param string $sClass
* @param string $sAttCode
* @param string $sTagCode
* @param string $sTagLabel
* @param string $sTagDescription
*
* @return \TagSetFieldData
* @throws \CoreException
*/
protected function CreateTagData($sClass, $sAttCode, $sTagCode, $sTagLabel, $sTagDescription = '')
{
$sTagClass = TagSetFieldData::GetTagDataClassName($sClass, $sAttCode);
$oTagData = $this->createObject($sTagClass, array(
'code' => $sTagCode,
'label' => $sTagLabel,
'obj_class' => $sClass,
'obj_attcode' => $sAttCode,
'description' => $sTagDescription,
));
$this->debug("Created {$oTagData->Get('code')} ({$oTagData->Get('label')})");
/** @var \TagSetFieldData $oTagData */
return $oTagData;
}
/**
* Create a Ticket in database
*
* @param string $sClass
* @param string $sAttCode
* @param string $sTagCode
*
* @throws \CoreException
*/
protected function RemoveTagData($sClass, $sAttCode, $sTagCode)
{
$sTagClass = TagSetFieldData::GetTagDataClassName($sClass, $sAttCode);
$this->RemoveObjects($sTagClass, "SELECT $sTagClass WHERE code = '$sTagCode'");
}
private function RemoveObjects($sClass, $sOQL)
{
$oFilter = DBSearch::FromOQL($sOQL);
$aRes = $oFilter->ToDataArray(array('id'));
foreach ($aRes as $aRow)
{
$this->debug($aRow);
$iKey = $aRow['id'];
if (!empty($iKey))
{
$oObject = MetaModel::GetObject($sClass, $iKey);
$oObject->DBDelete();
}
}
}
/**
* Create a UserRequest in database
*
* @param int $iNum
* @param int $iTimeSpent
* @param int $iOrgId
* @param int $iCallerId
*
* @return \UserRequest
* @throws Exception
*/
protected function CreateUserRequest($iNum, $iTimeSpent = 0, $iOrgId = 0, $iCallerId = 0)
{
/** @var \UserRequest $oTicket */
$oTicket = $this->createObject('UserRequest', array(
'ref' => 'Ticket_'.$iNum,
'title' => 'BUG 1161_'.$iNum,
//'request_type' => 'incident',
'description' => 'Add aggregate functions',
'time_spent' => $iTimeSpent,
'caller_id' => $iCallerId,
'org_id' => ($iOrgId == 0 ? $this->getTestOrgId() : $iOrgId),
));
$this->debug("Created {$oTicket->Get('title')} ({$oTicket->Get('ref')})");
return $oTicket;
}
/**
* Create a Server in database
*
* @param int $iNum
* @param null $iRackUnit
*
* @return Server
* @throws \Exception
*/
protected function CreateServer($iNum, $iRackUnit = null)
{
/** @var Server $oServer */
$oServer = $this->createObject('Server', array(
'name' => 'Server_'.$iNum,
'org_id' => $this->getTestOrgId(),
'nb_u' => $iRackUnit,
));
$this->debug("Created {$oServer->GetName()} ({$oServer->GetKey()})");
return $oServer;
}
/**
* Create a PhysicalInterface in database
*
* @param int $iNum
* @param int $iSpeed
* @param int $iConnectableCiId
*
* @return DBObject
* @throws Exception
*/
protected function CreatePhysicalInterface($iNum, $iSpeed, $iConnectableCiId)
{
$oObj = $this->createObject('PhysicalInterface', array(
'name' => "$iNum",
'speed' => $iSpeed,
'connectableci_id' => $iConnectableCiId,
));
$this->debug("Created {$oObj->GetName()} ({$oObj->GetKey()})");
return $oObj;
}
/**
* Create a FiberChannelInterface in database
*
* @param int $iNum
* @param int $iSpeed
* @param int $iConnectableCiId
*
* @return DBObject
* @throws Exception
*/
protected function CreateFiberChannelInterface($iNum, $iSpeed, $iConnectableCiId)
{
$oObj = $this->createObject('FiberChannelInterface', array(
'name' => "$iNum",
'speed' => $iSpeed,
'datacenterdevice_id' => $iConnectableCiId,
));
$this->debug("Created {$oObj->GetName()} ({$oObj->GetKey()})");
return $oObj;
}
/**
* Create a Person in database
*
* @param int $iNum
* @param int $iOrgId
*
* @return Person
* @throws Exception
*/
protected function CreatePerson($iNum, $iOrgId = 0)
{
/** @var Person $oPerson */
$oPerson = $this->createObject('Person', array(
'name' => 'Person_'.$iNum,
'first_name' => 'Test',
'org_id' => ($iOrgId == 0 ? $this->getTestOrgId() : $iOrgId),
));
$this->debug("Created {$oPerson->GetName()} ({$oPerson->GetKey()})");
return $oPerson;
}
/**
* @param string $sLogin
* @param int $iProfileId
*
* @return \DBObject
* @throws Exception
*/
protected function CreateUser($sLogin, $iProfileId, $sPassword=null)
{
if (empty($sPassword)){
$sPassword = $sLogin;
}
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('profileid', $iProfileId);
$oUserProfile->Set('reason', 'UNIT Tests');
$oSet = DBObjectSet::FromObject($oUserProfile);
$oUser = $this->createObject('UserLocal', array(
'contactid' => 2,
'login' => $sLogin,
'password' => $sPassword,
'language' => 'EN US',
'profile_list' => $oSet,
));
$this->debug("Created {$oUser->GetName()} ({$oUser->GetKey()})");
return $oUser;
}
/**
* @param \DBObject $oUser
* @param int $iProfileId
*
* @return \DBObject
* @throws Exception
*/
protected function AddProfileToUser($oUser, $iProfileId)
{
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('profileid', $iProfileId);
$oUserProfile->Set('reason', 'UNIT Tests');
/** @var DBObjectSet $oSet */
$oSet = $oUser->Get('profile_list');
$oSet->AddObject($oUserProfile);
$oUser = $this->updateObject('UserLocal', $oUser->GetKey(), array(
'profile_list' => $oSet,
));
$this->debug("Updated {$oUser->GetName()} ({$oUser->GetKey()})");
return $oUser;
}
/**
* Create a Hypervisor in database
*
* @param int $iNum
* @param Server $oServer
* @param Farm $oFarm
*
* @return Hypervisor
* @throws Exception
*/
protected function CreateHypervisor($iNum, $oServer, $oFarm = null)
{
/** @var Hypervisor $oHypervisor */
$oHypervisor = $this->createObject('Hypervisor', array(
'name' => 'Hypervisor_'.$iNum,
'org_id' => $this->getTestOrgId(),
'server_id' => $oServer->GetKey(),
'farm_id' => is_null($oFarm) ? 0 : $oFarm->GetKey(),
));
if (is_null($oFarm))
{
$this->debug("Created {$oHypervisor->GetName()} ({$oHypervisor->GetKey()}) on {$oServer->GetName()}");
}
else
{
$this->debug("Created {$oHypervisor->GetName()} ({$oHypervisor->GetKey()}) on {$oServer->GetName()} part of {$oFarm->GetName()}");
}
return $oHypervisor;
}
/**
* Create a Farm in database
*
* @param int $iNum
* @param string $sRedundancy
*
* @return Farm
* @throws Exception
*/
protected function CreateFarm($iNum, $sRedundancy = '1')
{
/** @var Farm $oFarm */
$oFarm = $this->createObject('Farm', array(
'name' => 'Farm_'.$iNum,
'org_id' => $this->getTestOrgId(),
'redundancy' => $sRedundancy,
));
$this->debug("Created {$oFarm->GetName()} ({$oFarm->GetKey()}) redundancy $sRedundancy");
return $oFarm;
}
/**
* Create a VM in database
*
* @param int $iNum
* @param VirtualHost $oVirtualHost
*
* @return VirtualMachine
* @throws Exception
*/
protected function CreateVirtualMachine($iNum, $oVirtualHost)
{
/** @var VirtualMachine $oVirtualMachine */
$oVirtualMachine = $this->createObject('VirtualMachine', array(
'name' => 'VirtualMachine_'.$iNum,
'org_id' => $this->getTestOrgId(),
'virtualhost_id' => $oVirtualHost->GetKey(),
));
$this->debug("Created {$oVirtualMachine->GetName()} ({$oVirtualMachine->GetKey()}) on {$oVirtualHost->GetName()}");
return $oVirtualMachine;
}
protected function CreateObjectWithTagSet()
{
$oFaqCategory = MetaModel::GetObject('FAQCategory', 1, false);
if (empty($oFaqCategory))
{
$oFaqCategory = $this->createObject('FAQCategory', array(
'name' => 'FAQCategory_phpunit',
));
}
/** @var \FAQ $oFaq */
$oFaq = $this->createObject('FAQ', array(
'category_id' => $oFaqCategory->GetKey(),
'title' => 'FAQ_phpunit',
));
$this->debug("Created {$oFaq->GetName()}");
return $oFaq;
}
/**
* Add a link between a contact and a CI.
* The database is not updated.
*
* @param Contact $oContact
* @param FunctionalCI $oCI
*
* @return lnkContactToFunctionalCI
* @throws Exception
*/
protected function AddContactToCI($oContact, $oCI)
{
$oNewLink = new lnkContactToFunctionalCI();
$oNewLink->Set('contact_id', $oContact->GetKey());
$oContacts = $oCI->Get('contacts_list');
$oContacts->AddItem($oNewLink);
$oCI->Set('contacts_list', $oContacts);
$this->debug("Added {$oContact->GetName()} to {$oCI->GetName()}");
return $oNewLink;
}
/**
* Remove a link between a contact and a CI.
* The database is not updated.
*
* @param Contact $oContact
* @param FunctionalCI $oCI
*
* @throws Exception
*/
protected function RemoveContactFromCI($oContact, $oCI)
{
$oContacts = $oCI->Get('contacts_list');
foreach ($oContacts as $oLnk)
{
if ($oLnk->Get('contact_id') == $oContact->GetKey())
{
$oContacts->RemoveItem($oLnk->GetKey());
$oCI->Set('contacts_list', $oContacts);
$this->debug("Removed {$oContact->GetName()} from {$oCI->Get('name')}");
return;
}
}
}
/**
* Add a link between a CI and a Ticket.
* The database is not updated.
*
* @param FunctionalCI $oCI
* @param Ticket $oTicket
* @param string $sImpactCode
*
* @return array
* @throws Exception
*/
protected function AddCIToTicket($oCI, $oTicket, $sImpactCode)
{
$oNewLink = new lnkFunctionalCIToTicket();
$oNewLink->Set('functionalci_id', $oCI->GetKey());
$oNewLink->Set('impact_code', $sImpactCode);
$oCIs = $oTicket->Get('functionalcis_list');
$oCIs->AddItem($oNewLink);
$oTicket->Set('functionalcis_list', $oCIs);
$this->debug("Added {$oCI->GetName()} to {$oTicket->Get('ref')} with {$sImpactCode}");
return array($oCI->GetKey() => $sImpactCode);
}
/**
* Remove a link between a CI and a Ticket.
* The database is not updated.
*
* @param FunctionalCI $oCI
* @param Ticket $oTicket
*
* @throws Exception
*/
protected function RemoveCIFromTicket($oCI, $oTicket)
{
$oCIs = $oTicket->Get('functionalcis_list');
foreach ($oCIs as $oLnk)
{
if ($oLnk->Get('functionalci_id') == $oCI->GetKey())
{
$sImpactCode = $oLnk->Get('impact_code');
$oCIs->RemoveItem($oLnk->GetKey());
$oTicket->Set('functionalcis_list', $oCIs);
$this->debug("Removed {$oCI->GetName()} from {$oTicket->Get('ref')} ({$sImpactCode})");
return;
}
}
$this->debug("ERROR: {$oCI->GetName()} not attached to {$oTicket->Get('ref')}");
$this->assertTrue(false);
}
/**
* Add a link between a Contact and a Ticket.
* The database is not updated.
*
* @param Contact $oContact
* @param Ticket $oTicket
* @param string $sRoleCode
* @param array $aParams
*
* @return array
* @throws Exception
*/
protected function AddContactToTicket($oContact, $oTicket, $sRoleCode, $aParams = array())
{
$oNewLink = new lnkContactToTicket();
$oNewLink->Set('contact_id', $oContact->GetKey());
$oNewLink->Set('role_code', $sRoleCode);
foreach ($aParams as $sAttCode => $oValue)
{
$oNewLink->Set($sAttCode, $oValue);
}
$oCIs = $oTicket->Get('contacts_list');
$oCIs->AddItem($oNewLink);
$oTicket->Set('contacts_list', $oCIs);
$this->debug("Added {$oContact->GetName()} to {$oTicket->Get('ref')} with {$sRoleCode}");
return array($oContact->GetKey() => $sRoleCode);
}
/**
* Remove a link between a Contact and a Ticket.
* The database is not updated.
*
* @param Contact $oContact
* @param Ticket $oTicket
*
* @throws Exception
*/
protected function RemoveContactFromTicket($oContact, $oTicket)
{
$oContacts = $oTicket->Get('contacts_list');
foreach ($oContacts as $oLnk)
{
if ($oLnk->Get('contact_id') == $oContact->GetKey())
{
$sRoleCode = $oLnk->Get('role_code');
$oContacts->RemoveItem($oLnk->GetKey());
$oTicket->Set('contacts_list', $oContacts);
$this->debug("Removed {$oContact->GetName()} from {$oTicket->Get('ref')} ({$sRoleCode})");
return;
}
}
}
/**
* Reload a Ticket from the database.
*
* @param DBObject $oObject
*
* @return \DBObject|null
* @throws ArchivedObjectException
* @throws Exception
*/
protected function ReloadObject(&$oObject)
{
$oObject = MetaModel::GetObject(get_class($oObject), $oObject->GetKey());
return $oObject;
}
/**
* Check or Display the CI list of a Ticket.
*
* @param Ticket $oTicket
* @param array $aWaitedCIList { iCIId => sImpactCode }
*
* @throws Exception
*/
protected function CheckFunctionalCIList($oTicket, $aWaitedCIList = array())
{
$this->debug("\nResulting functionalcis_list {$oTicket->Get('ref')} ({$oTicket->Get('functionalcis_list')->Count()}):");
foreach ($oTicket->Get('functionalcis_list') as $oLnk)
{
$this->debug($oLnk->Get('functionalci_name')." => ".$oLnk->Get('impact_code')."");
$iId = $oLnk->Get('functionalci_id');
if (!empty($aWaitedCIList))
{
$this->assertTrue(array_key_exists($iId, $aWaitedCIList));
$this->assertEquals($aWaitedCIList[$iId], $oLnk->Get('impact_code'));
}
}
}
/**
* Check or Display the Contact list of a DBObject (having a contacts_list).
* Can also control other attributes of the link.
*
* @param Ticket $oTicket
* @param array $aWaitedContactList { iContactId => array(attcode => value) }
*
* @throws Exception
*/
protected function CheckContactList($oTicket, $aWaitedContactList = array())
{
$this->debug("\nResulting contacts_list {$oTicket->Get('ref')} ({$oTicket->Get('contacts_list')->Count()}):");
foreach ($oTicket->Get('contacts_list') as $oLnk)
{
$this->debug($oLnk->Get('contact_id_friendlyname')." => ".$oLnk->Get('role_code'));
$iId = $oLnk->Get('contact_id');
if (!empty($aWaitedContactList))
{
$this->assertTrue(array_key_exists($iId, $aWaitedContactList));
foreach ($aWaitedContactList[$iId] as $sAttCode => $oValue)
{
if (MetaModel::IsValidAttCode(get_class($oTicket), $sAttCode))
{
$this->assertEquals($oValue, $oLnk->Get($sAttCode));
}
}
}
}
}
protected function CreateTestOrganization()
{
// Create a specific organization for the tests
$oOrg = $this->CreateOrganization('UnitTestOrganization');
$this->iTestOrgId = $oOrg->GetKey();
}
}

View File

@@ -0,0 +1,238 @@
<?php
/*
* Copyright (C) 2013-2021 Combodo SARL
* 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;
use Combodo\iTop\Test\TestUtils\RunTimeEnvironment\RunTimeEnvironmentTest;
use PHPUnit\Framework\TestCase;
use SetupUtils;
define('DEBUG_UNIT_TEST', true);
class ItopTestCase extends TestCase
{
const TEST_LOG_DIR = 'test';
const TEST_ITOP_ENV_DEFAULT = null;
const TEST_ITOP_ENV_PREFIX = 'test-';
CONST TEST_TARGET_BASE_PATH = '/test/testUtils/conf/targets/';
protected function setUp()
{
@include_once '../approot.inc.php';
@include_once '../../approot.inc.php';
@include_once '../../../approot.inc.php';
@include_once '../../../../approot.inc.php';
@include_once '../../../../../approot.inc.php';
@include_once '../../../../../../approot.inc.php';
@include_once '../../../../../../../approot.inc.php';
@include_once '../../../../../../../../approot.inc.php';
@include_once '../../../../../../../../../approot.inc.php';
$this->debug("\n----------\n---------- ".$this->getName()."\n----------\n");
$this->SetupItopEnv(static::TEST_ITOP_ENV_DEFAULT);
}
/**
* This method MUST be runned before the MetaModel startup
*
* NB: The startup is generally performed by `application/startup.inc.php`
*
*/
public function SetupItopEnv($sITopEnv)
{
if (defined('MODULESROOT')) {
throw new \Exception('setupItopEnv must be called before the MetaModel startup!');
}
if ($sITopEnv != null) {
if (empty($_SESSION)) {
session_name('itop-'.md5(APPROOT));
session_start();
session_write_close();
}
$_SESSION['itop_env'] = ItopTestCase::TEST_ITOP_ENV_PREFIX."$sITopEnv";
$_REQUEST['switch_env'] = ItopTestCase::TEST_ITOP_ENV_PREFIX."$sITopEnv";
$this->BuildItopEnv($sITopEnv);
}
}
private function BuildItopEnv($sITopEnv)
{
if (is_dir(APPROOT."/env-".ItopTestCase::TEST_ITOP_ENV_PREFIX.$sITopEnv)) {
//The env has already been built
return;
}
if (!is_dir(APPROOT.self::TEST_TARGET_BASE_PATH."/{$sITopEnv}")) {
throw new \Exception("iTop env '{$sITopEnv}' not found");
}
$oRuntimeEnv = new RunTimeEnvironmentTest($sITopEnv);
$oRuntimeEnv->PushDelta();
$oRuntimeEnv->PushModules();
$oRuntimeEnv->CheckDirectories();
$oRuntimeEnv->SmartCompile();
$oConfig = $oRuntimeEnv->MakeConfigFile($sITopEnv.' (built on '.date('Y-m-d').')');
$oConfig->Set('access_mode', ACCESS_FULL);
$sBdName = $oConfig->Get('db_name');
assert(false !== strpos($sBdName, str_replace('-', '_', static::TEST_ITOP_ENV_DEFAULT)), 'The DB contains the test env');
$oRuntimeEnv->PrepareEmptyDatabase($sBdName);
$oRuntimeEnv->WriteConfigFileSafe($oConfig);
$oRuntimeEnv->InitDataModel($oConfig, true);
// Safety check: check the inter dependencies, will throw an exception in case of inconsistency
$aAvailableModules = $oRuntimeEnv->AnalyzeInstallation($oConfig, $oRuntimeEnv->GetBuildDir(), true);
$oRuntimeEnv->CheckMetaModel(); // Will throw an exception if a problem is detected
$oRuntimeEnv->Commit();
$aSelectedModules = array();
foreach ($aAvailableModules as $sModuleId => $aModule)
{
if (($sModuleId == ROOT_MODULE) || ($sModuleId == DATAMODEL_MODULE))
{
continue;
}
else
{
$aSelectedModules[] = $sModuleId;
}
}
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'BeforeDatabaseCreation');
$oRuntimeEnv->CreateDatabaseStructure($oConfig, 'install');
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseCreation');
$oRuntimeEnv->UpdatePredefinedObjects();
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDatabaseSetup');
$oRuntimeEnv->LoadData($aAvailableModules, $aSelectedModules, false /* no sample data*/);
$oRuntimeEnv->CallInstallerHandlers($aAvailableModules, $aSelectedModules, 'AfterDataLoad');
// Record the installation so that the "about box" knows about the installed modules
$sDataModelVersion = $oRuntimeEnv->GetCurrentDataModelVersion();
$oExtensionsMap = new \iTopExtensionsMap();
// Default choices = as before
$oExtensionsMap->LoadChoicesFromDatabase($oConfig);
foreach ($oExtensionsMap->GetAllExtensions() as $oExtension)
{
// Plus all "remote" extensions
if ($oExtension->sSource == \iTopExtension::SOURCE_REMOTE)
{
$oExtensionsMap->MarkAsChosen($oExtension->sCode);
}
}
$aSelectedExtensionCodes = array();
foreach ($oExtensionsMap->GetChoices() as $oExtension)
{
$aSelectedExtensionCodes[] = $oExtension->sCode;
}
$aSelectedExtensions = $oExtensionsMap->GetChoices();
$oRuntimeEnv->RecordInstallation($oConfig, $sDataModelVersion, $aSelectedModules, $aSelectedExtensionCodes, 'Done by ItopTEstCase');
}
protected function debug($sMsg)
{
if (DEBUG_UNIT_TEST)
{
if (is_string($sMsg))
{
echo "$sMsg\n";
}
else
{
print_r($sMsg);
}
}
}
public function GetMicroTime()
{
list($uSec, $sec) = explode(" ", microtime());
return ((float)$uSec + (float)$sec);
}
public function WriteToCsvHeader($sFilename, $aHeader)
{
$sResultFile = APPROOT.'log/'.$sFilename;
if (is_file($sResultFile))
{
@unlink($sResultFile);
}
SetupUtils::builddir(dirname($sResultFile));
file_put_contents($sResultFile, implode(';', $aHeader)."\n");
}
public function WriteToCsvData($sFilename, $aData)
{
$sResultFile = APPROOT.'log/'.$sFilename;
$file = fopen($sResultFile, 'a');
fputs($file, implode(';', $aData)."\n");
fclose($file);
}
public function GetTestId()
{
$sId = str_replace('"', '', $this->getName());
$sId = str_replace(' ', '_', $sId);
return $sId;
}
public function InvokeNonPublicStaticMethod($sObjectClass, $sMethodName, $aArgs)
{
return $this->InvokeNonPublicMethod($sObjectClass, $sMethodName, null, $aArgs);
}
/**
* @param string $sObjectClass for example DBObject::class
* @param string $sMethodName
* @param object $oObject
* @param array $aArgs
*
* @return mixed method result
*
* @throws \ReflectionException
*/
public function InvokeNonPublicMethod($sObjectClass, $sMethodName, $oObject, $aArgs)
{
$class = new \ReflectionClass($sObjectClass);
$method = $class->getMethod($sMethodName);
$method->setAccessible(true);
return $method->invokeArgs($oObject, $aArgs);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* Copyright (C) 2013-2021 Combodo SARL
* 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\TestUtils;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\TestSuite;
use Symfony\Component\Filesystem\Filesystem;
class PurgeTestEnvDir implements TestListener
{
use TestListenerDefaultImplementation;
private $bIsFirstStartCall = true;
private $bIsFirstEndCall = true;
public function startTestSuite(TestSuite $suite)
{
if (!$this->bIsFirstStartCall) {
return;
}
$this->bIsFirstStartCall = false;
$filesystem = new Filesystem();
$prefix = \Combodo\iTop\Test\UnitTest\ItopTestCase::TEST_ITOP_ENV_PREFIX;
assert(strlen($prefix) > 4, 'the env test prefix is long enough');
$sBaseDir = realpath(__DIR__."/../../../");
$envPathPattern = "{$sBaseDir}/env-{$prefix}*";
$aListEnv = glob($envPathPattern, GLOB_ONLYDIR);
foreach ($aListEnv as $sPath) {
assert(strpos($sPath, 'env-test-') !== false, 'the deleted dir contains env-test-');
$filesystem->remove($sPath);
fwrite(STDOUT, sprintf('PurgeTestEnvDir: test env "%s" purged before the Tests', basename($sPath)));
fwrite(STDOUT, "\n");
}
}
}

2
test/testUtils/README.md Normal file
View File

@@ -0,0 +1,2 @@
# About this directory
This directory `testUtils` is meant to contain misc. files required by the test, but no test at all.

View File

@@ -0,0 +1,635 @@
<?php
/*
* Copyright (C) 2013-2021 Combodo SARL
* 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\TestUtils\RunTimeEnvironment;
use CMDBSource;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use Config;
use Exception;
use iTopExtension;
use MFCompiler;
use MFCoreModule;
use MFDeltaModule;
use MFDictModule;
use ModelFactory;
use MySQLException;
use MySQLHasGoneAwayException;
use SetupInfo;
use SetupUtils;
use utils;
require_once APPROOT.'setup/runtimeenv.class.inc.php';
require_once APPROOT.'setup/xmldataloader.class.inc.php';
class RunTimeEnvironmentTest extends \RunTimeEnvironment
{
protected $sEnvironmentSuffixe;
private $sTargetPath;
private $aTargetConfig;
public function __construct($sEnvironment)
{
$this->sEnvironmentSuffixe = $sEnvironment;
$this->sTargetPath = APPROOT."/test/testUtils/conf/targets/{$this->sEnvironmentSuffixe}/";
$sRealEnvironment = ItopTestCase::TEST_ITOP_ENV_PREFIX.$this->sEnvironmentSuffixe;
mkdir(APPROOT.'/env-'.$sRealEnvironment);
$config = "{$this->sTargetPath}/target.ini";
if (! is_file($config)) {
$this->aTargetConfig = [];
} else {
$this->aTargetConfig = parse_ini_file($config);
}
parent::__construct($sRealEnvironment);
}
private function GetTargetConfig($key, $default = null)
{
if (!isset($this->aTargetConfig[$key])) {
return $default;
}
$search = [
'$APPROOT$',
];
$replace = [
APPROOT,
];
if (is_string($this->aTargetConfig[$key])) {
return str_replace($search, $replace, $this->aTargetConfig[$key]);
}
if (is_array($this->aTargetConfig[$key])) {
$aResultReplaced = $this->aTargetConfig[$key];
foreach ($aResultReplaced as $i => $sVal) {
if (is_string($sVal)) {
$aResultReplaced[$i] = str_replace($search, $replace, $sVal);
}
}
return $aResultReplaced;
}
return $this->aTargetConfig[$key];
}
public function PushDelta()
{
// This is the real standard, that will be taken into account by the compiler + backup/restore
$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
$sPreviousDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.prev.xml';
if (!file_exists(APPROOT.'data/test'))
{
mkdir(APPROOT.'data/test');
}
if (!file_exists(APPROOT.'data/test/'.$this->sTargetEnv))
{
mkdir(APPROOT.'data/test/'.$this->sTargetEnv);
}
if (file_exists($sDeltaFile))
{
// to be restored in case an issue is encountered later on
copy($sDeltaFile, $sPreviousDeltaFile);
}
$sDeltaPath = "{$this->sTargetPath}/delta.xml";
// fwrite(STDERR, __METHOD__.':'.var_export([
// '$sDeltaPath' => $sDeltaPath,
// 'is_file($sDeltaPath)' => is_file($sDeltaPath),
// '$sDeltaFile' => $sDeltaFile,
// ], true));
if (is_file($sDeltaPath)) {
copy($sDeltaPath, $sDeltaFile);
} elseif (is_file($sDeltaFile)) {
unlink($sDeltaFile);
}
}
public function RestorePreviousDelta()
{
$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
$sPreviousDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.prev.xml';
unlink($sDeltaFile);
if (file_exists($sPreviousDeltaFile))
{
rename($sPreviousDeltaFile, $sDeltaFile);
}
}
public function PushModules()
{
$sSourceDir = "{$this->sTargetPath}/{$this->sFinalEnv}/modules/";
$sModulesDir = APPROOT.'data/'.$this->sTargetEnv.'-modules/';
self::MakeDirSafe($sModulesDir);
SetupUtils::tidydir($sModulesDir);
SetupUtils::copydir($sSourceDir, $sModulesDir);
}
public function IsInstalled()
{
$sConfig = APPCONF.$this->sTargetEnv.'/'.ITOP_CONFIG_FILE;
if (file_exists($sConfig))
{
return true;
}
else
{
return false;
}
}
public function GetInstalledModules($sSourceEnv, $sSourceDir)
{
return parent::GetMFModulesToCompile($sSourceEnv, $sSourceDir);
}
public function MakeConfigFile($sEnvironmentLabel = null)
{
$oConfig = $this->GetConfig();
if (!is_null($oConfig))
{
// Return the existing one
$oConfig->UpdateIncludes($this->sTargetEnv);
}
else
{
// Clone the default 'production' config file
//
$oConfig = clone($this->GetConfig('production'));
$oConfig->UpdateIncludes($this->sTargetEnv);
if (is_null($sEnvironmentLabel))
{
$sEnvironmentLabel = $this->sTargetEnv;
}
$oConfig->Set('app_env_label', $sEnvironmentLabel, 'test');
if ($this->sFinalEnv !== 'production')
{
$oConfig->Set('db_name', $oConfig->Get('db_name').'_'.str_replace('-', '_', $this->sFinalEnv));
}
}
return $oConfig;
}
protected function GetConfig($sEnvironment = null)
{
if (is_null($sEnvironment))
{
$sEnvironment = $this->sTargetEnv;
}
$sFile = APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
if (file_exists($sFile))
{
$oConfig = new Config($sFile);
return $oConfig;
}
else
{
return null;
}
}
public function PrepareEmptyDatabase($sBdName)
{
try {
\CMDBSource::DropDB($sBdName);
} catch (\MySQLException $e) {
//mysql errno 1008: Can't drop database 'xxx'; database doesn't exist
if ($e->getCode() != 1008) {
throw $e;
}
}
\CMDBSource::CreateDB($sBdName);
\CMDBSource::SelectDB($sBdName);
}
public function CloneDatabase($sSourceEnv = 'production')
{
if ($sSourceEnv == $this->sTargetEnv)
{
throw new Exception("Attempting to clone the DB from the environment '$sSourceEnv' into itself!");
}
$oSourceConfig = $this->GetConfig($sSourceEnv);
// Copy the 'production' database to the target environment (new_db_name)
//$oP = new ajax_page('');
$sOldDBName = $oSourceConfig->Get('db_name');
$sPrefix = $oSourceConfig->Get('db_subname');
// No need to specify the DB to use, the name will be used in each command
CMDBSource::InitFromConfig($oSourceConfig);
$sNewDBName = $oSourceConfig->Get('db_name').'_'.$this->sFinalEnv;
try
{
CMDBSource::DropDB($sNewDBName);
}
catch(MySQLException $e)
{
// Database may not already exist, never mind...
// at least it will be clean !!
}
try
{
CMDBSource::CreateDB($sNewDBName);
}
catch(MySQLException $e)
{
// Database may already exist, never mind...
}
//Parcourir la liste des tables utilisées par iTop (charger le data model de la prod, ou considérer l'ensemble des tables préfixées ????, ou voir le XML)
// MySQL 5.0.2 will support this:
// "SHOW FULL TABLES FROM `$sOldDBName` LIKE '$sPrefix%' WHERE Table_type = 'BASE TABLE'"
$aTables = CMDBSource::QueryToArray("SHOW TABLES FROM `$sOldDBName` LIKE '$sPrefix%'");
$sViewPrefix = $sPrefix.'view_';
foreach ($aTables as $aRow)
{
$sTableName = $aRow[0];
if (substr($sTableName, 0, strlen($sViewPrefix)) != $sViewPrefix)
{
// do not copy views
try
{
CMDBSource::Query("DROP TABLE IF EXISTS `$sNewDBName`.`$sTableName`");
CMDBSource::Query("CREATE TABLE `$sNewDBName`.`$sTableName` ENGINE=".MYSQL_ENGINE." DEFAULT CHARSET=".DEFAULT_CHARACTER_SET." COLLATE=".DEFAULT_COLLATION." SELECT * FROM `$sOldDBName`.`$sTableName`");
} catch (MySQLHasGoneAwayException $e)
{
throw new Exception("Failed to clone the DB for table '$sTableName'. The database parameter 'max_allowed_paquet' may be too small.");
} catch (Exception $e)
{
throw new Exception("Failed to clone the DB for table '$sTableName'");
}
}
}
}
public function JumpInto($oPage = null)
{
$sTargetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?switch_env='.$this->sFinalEnv;
if (is_null($oPage))
{
header('Location: '.$sTargetUrl);
exit;
}
else
{
$oPage->add_ready_script("window.location.href='$sTargetUrl';");
}
}
public function CheckDirectories()
{
$sTargetDir = APPROOT.$this->sFinalEnv;
$sBuildDir = $sTargetDir.'-build';
self::CheckDirectory($sTargetDir);
self::CheckDirectory($sBuildDir);
}
/**
* @param $sDir
* @throws Exception
*/
public static function CheckDirectory($sDir)
{
if (!is_dir($sDir))
{
if (!@mkdir($sDir,0770))
{
throw new Exception('Creating directory '.$sDir.' is denied (Check access rights)');
}
}
// Try create a file
$sTempFile = $sDir.'/__itop_temp_file__';
if (!@touch($sTempFile))
{
throw new Exception('Write access to '.$sDir.' is denied (Check access rights)');
}
@unlink($sTempFile);
}
public function SmartCompile()
{
$sCompileFrom = $this->GetTargetConfig('compileFrom');
$bUseSymLinks = false;
$sTargetDir = APPROOT.'env-'.$this->sTargetEnv;
$bSkipTempDir = ($this->sFinalEnv != $this->sTargetEnv); // No need for a temporary directory if sTargetEnv is already a temporary directory
$sConfigPath = $this->compileGetBaseConfigPath();
$oSourceConfig = new Config($sConfigPath);
if (null == $sCompileFrom) {
$sSourceDir = null;
} else {
$sSourceDir = $oSourceConfig->Get('source_dir');
}
$sSourceDirFull = APPROOT.$oSourceConfig->Get('source_dir');
// Do load the required modules
//
$oFactory = new ModelFactory($sSourceDirFull);
$aModulesToCompile = $this->GetMFModulesToCompile($sCompileFrom, $sSourceDir);
foreach($aModulesToCompile as $oModule)
{
if ($oModule instanceof MFDeltaModule)
{
// Just before loading the delta, let's save an image of the datamodel
// in case there is no delta the operation will be done after the end of the loop
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
}
$oFactory->LoadModule($oModule);
}
if ($oModule instanceof MFDeltaModule)
{
// A delta was loaded, let's save a second copy of the datamodel
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'-with-delta.xml');
}
else
{
// No delta was loaded, let's save the datamodel now
$oFactory->SaveToFile(APPROOT.'data/datamodel-'.$this->sTargetEnv.'.xml');
}
// fwrite(STDERR, 'Compile:'.var_export([
// '$this->sTargetEnv' => $this->sTargetEnv,
// '$this->sFinalEnv' => $this->sFinalEnv,
// '$sCompileFrom' => $sCompileFrom,
// '$bUseSymLinks' => $bUseSymLinks,
// '$sConfigPath' => $sConfigPath,
// '$sSourceDirFull' => $sSourceDirFull,
// 'array_keys($aModulesToCompile)' => array_keys($aModulesToCompile),
//// '$aModulesToCompile[""]-> GetDataModelFiles()' => isset($aModulesToCompile['']) ? $aModulesToCompile['']-> GetDataModelFiles() : null,
// 'get_class(end($aModulesToCompile))' => get_class(end($aModulesToCompile)),
// 'end($aModulesToCompile)->GetDataModelFiles()' => end($aModulesToCompile)->GetDataModelFiles(),
// ], true));
self::MakeDirSafe($sTargetDir);
$oMFCompiler = new MFCompiler($oFactory, $this->sFinalEnv);
$oMFCompiler->Compile($sTargetDir, null, $bUseSymLinks, $bSkipTempDir);
$sCacheDir = APPROOT.'data/cache-'.$this->sTargetEnv;
SetupUtils::builddir($sCacheDir);
SetupUtils::tidydir($sCacheDir);
\MetaModel::ResetCache(md5(APPROOT).'-'.$this->sTargetEnv);
return array_keys($aModulesToCompile);
}
private function compileGetBaseConfigPath()
{
$sCompileFrom = $this->GetTargetConfig('baseConfig');
if (null != $sCompileFrom) {
return $sCompileFrom;
}
$sConfigPath = "{$this->sTargetPath}/config-itop.php";
if (is_file($sConfigPath)) {
return $sConfigPath;
}
$sCompileFrom = $this->GetTargetConfig('compileFrom');
if (null != $sCompileFrom) {
return utils::GetConfigFilePath($sCompileFrom);
}
}
protected function GetMFModulesToCompile($sCompileFrom, $sSourceDir)
{
$aDirsToCompile = $this->GetTargetModules();
$aModulesSelected = $this->GetTargetConfig('module_select', []);
$sExtraDir = "{$this->sTargetPath}/modules";
if (is_dir($sExtraDir))
{
$aDirsToCompile[] = $sExtraDir;
}
if (null != $sSourceDir) {
$sSourceDirFull = APPROOT.$sSourceDir;
if (is_dir($sSourceDirFull))
{
$aDirsToCompile[] = $sSourceDirFull;
}
}
if (is_dir(APPROOT.'extensions'))
{
$aDirsToCompile[] = APPROOT.'extensions';
}
$aExtraDirs = $this->GetExtraDirsToScan($aDirsToCompile);
$aDirsToCompile = array_merge($aDirsToCompile, $aExtraDirs);
$aRet = array();
// Actually read the modules available for the target environment,
// but get the selection from the source environment and finally
// mark as (automatically) chosen all the "remote" modules present in the
// target environment (data/<target-env>-modules)
// The actual choices will be recorded by RecordInstallation below
$this->oExtensionsMap = new \iTopExtensionsMap($this->sTargetEnv, true, $aExtraDirs);
// Determine the installed modules and extensions
//
$sConfigPath = $this->compileGetBaseConfigPath();
$oSourceConfig = new Config($sConfigPath);
$this->oExtensionsMap->LoadChoicesFromDatabase($oSourceConfig);
foreach($this->oExtensionsMap->GetAllExtensions() as $oExtension)
{
if($this->IsExtensionSelected($oExtension))
{
$this->oExtensionsMap->MarkAsChosen($oExtension->sCode);
}
}
// Do load the required modules
//
$oDictModule = new MFDictModule('dictionaries', 'iTop Dictionaries', APPROOT.'dictionaries');
$aRet[$oDictModule->GetName()] = $oDictModule;
$oFactory = new ModelFactory($aDirsToCompile);
$sDeltaFile = APPROOT.'core/datamodel.core.xml';
if (file_exists($sDeltaFile))
{
$oCoreModule = new MFCoreModule('core', 'Core Module', $sDeltaFile);
$aRet[$oCoreModule->GetName()] = $oCoreModule;
}
$sDeltaFile = APPROOT.'application/datamodel.application.xml';
if (file_exists($sDeltaFile))
{
$oApplicationModule = new MFCoreModule('application', 'Application Module', $sDeltaFile);
$aRet[$oApplicationModule->GetName()] = $oApplicationModule;
}
//here, it seem, that the source's RunTimeEnvironment has to be launch in order to acquire it's `$aAvailableModules` via an `AnalyzeInstallation`
if (null != $sCompileFrom) {
$oSourceEnv = new \RunTimeEnvironment($sCompileFrom);
$aAvailableModules = $oSourceEnv->AnalyzeInstallation($oSourceConfig, $aDirsToCompile);
} else {
$aAvailableModules = [];
}
$aModules = $oFactory->FindModules();
foreach($aModules as $oModule)
{
$sModule = $oModule->GetName();
$sModuleRootDir = $oModule->GetRootDir();
$bIsExtra = $this->oExtensionsMap->ModuleIsChosenAsPartOfAnExtension($sModule, iTopExtension::SOURCE_REMOTE);
if (in_array($sModule, $aModulesSelected)) {
$aRet[$oModule->GetName()] = $oModule;
} elseif (array_key_exists($sModule, $aAvailableModules))
{
if (($aAvailableModules[$sModule]['version_db'] != '') || $bIsExtra && !$oModule->IsAutoSelect()) //Extra modules are always unless they are 'AutoSelect'
{
$aRet[$oModule->GetName()] = $oModule;
}
}
}
// Now process the 'AutoSelect' modules
do
{
// Loop while new modules are added...
$bModuleAdded = false;
foreach($aModules as $oModule)
{
if (!array_key_exists($oModule->GetName(), $aRet) && $oModule->IsAutoSelect())
{
try
{
$bSelected = false;
SetupInfo::SetSelectedModules($aRet);
eval('$bSelected = ('.$oModule->GetAutoSelect().');');
}
catch(Exception $e)
{
$bSelected = false;
}
if ($bSelected)
{
$aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module
$bModuleAdded = true;
}
}
}
}
while($bModuleAdded);
$sDeltaFile = APPROOT.'data/'.$this->sTargetEnv.'.delta.xml';
if (file_exists($sDeltaFile))
{
$oDelta = new MFDeltaModule($sDeltaFile);
$aRet[$oDelta->GetName()] = $oDelta;
}
// fwrite(STDERR, __METHOD__.':'.var_export([
// '$this->sTargetEnv' => $this->sTargetEnv,
// '$sCompileFrom' => $sCompileFrom,
// '$sConfigPath' => $sConfigPath,
// '$sSourceDir' => $sSourceDir,
// '$aDirsToCompile' => $aDirsToCompile,
// 'array_keys($aRet)' => array_keys($aRet),
// '$aRet[""]-> GetDataModelFiles()' => isset($aRet['']) ? $aRet['']-> GetDataModelFiles() : null,
// ], true));
return $aRet;
}
/**
* @param Config $oConfig
*
* @throws \Exception
*/
public function WriteConfigFileSafe($oConfig)
{
self::MakeDirSafe(APPCONF);
self::MakeDirSafe(APPCONF.$this->sTargetEnv);
$sTargetConfigFile = APPCONF.$this->sTargetEnv.'/'.ITOP_CONFIG_FILE;
// Write the config file
@chmod($sTargetConfigFile, 0770); // In case it exists: RWX for owner and group, nothing for others
$oConfig->WriteToFile($sTargetConfigFile);
@chmod($sTargetConfigFile, 0660); // let be less extremist since we will need to remove it on each testSuite launch
}
/**
* Wrappers for logging
*/
protected $aLog = array();
protected function log_error($sText)
{
$this->aLog[] = "Error: $sText";
}
protected function log_warning($sText)
{
$this->aLog[] = "Warning: $sText";
}
protected function log_info($sText)
{
$this->aLog[] = "Info: $sText";
}
protected function log_ok($sText)
{
$this->aLog[] = "OK: $sText";
}
/**
* @return array
*/
protected function GetTargetModules()
{
$aTargetModules = [];
$aModulePath = $this->GetTargetConfig('module_path', []);
foreach ($aModulePath as $sModulePath) {
$aTargetModules[] = $sModulePath;
}
return $aTargetModules;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* Copyright (C) 2013-2021 Combodo SARL
* 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
*/
@require_once(__DIR__.'/ItopTestCase.php');
@require_once(__DIR__.'/ItopDataTestCase.php');
@require_once(__DIR__.'/../vendor/autoload.php');
@require_once(__DIR__.'/../../lib/autoload.php');

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<menus>
<menu id="module-test-foo" xsi:type="WebPageMenuNode" _delta="define">
<rank>9999</rank>
<parent>SystemTools</parent>
<url>foo.php</url>
<enable_admin_only>1</enable_admin_only>
</menu>
</menus>
</itop_design>

View File

@@ -0,0 +1,28 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*
* 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
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
Dict::Add('EN US', 'English', 'English', array(
'module-test-foo-keyword' => 'I am translated',
));

View File

@@ -0,0 +1,16 @@
<?php
// Copyright (C) 2014-2021 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

View File

@@ -0,0 +1,50 @@
<?php
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'module-test-foo/0.0.1',
array(
// Identification
//
'label' => 'Foo module',
'category' => 'Application management',
// Setup
//
'dependencies' => array(
),
'mandatory' => true,
'visible' => false,
// Components
//
'datamodel' => array(
'main.module-test-foo.php',
'model.module-test-foo.php',
),
'webservice' => array(
//'webservices.module-test-foo.php',
),
'dictionary' => array(
'en.dict.module-test-foo.php',
),
'data.struct' => array(
//'data.struct.module-test-foo.xml',
),
'data.sample' => array(
//'data.sample.module-test-foo.xml',
),
// Documentation
//
'doc.manual_setup' => '',
'doc.more_information' => '',
// Default settings
//
'settings' => array(
'module-test-foo' => 'bar',
),
)
);

View File

@@ -0,0 +1,4 @@
[main]
compileFrom = 'production'
#module_path[] = ''

View File

@@ -0,0 +1,16 @@
[main]
compileFrom = ''
baseConfig = '$APPROOT$/conf/production/config-itop.php'
module_path[] = '$APPROOT$/datamodels/2.x/authent-local/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-config/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-config-mgmt/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-profiles-itil/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-welcome-itil/'
module_select[] = 'authent-local'
module_select[] = 'itop-config'
module_select[] = 'itop-config-mgmt'
module_select[] = 'itop-profiles-itil'
module_select[] = 'itop-welcome-itil'

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.7">
<menus>
<menu id="withDeltaXml" xsi:type="WebPageMenuNode" _delta="define">
<rank>42</rank>
<parent>SystemTools</parent>
<url>withDeltaXml.php</url>
<enable_admin_only>1</enable_admin_only>
</menu>
</menus>
<dictionaries>
<dictionnary id="EN US">
<entries>
<entry id="module-test-foo-keyword" _delta="define">I am translated</entry>
</entries>
</dictionnary>
</dictionaries>
</itop_design>

View File

@@ -0,0 +1,15 @@
[main]
compileFrom = ''
baseConfig = '$APPROOT$/conf/production/config-itop.php'
module_path[] = '$APPROOT$/datamodels/2.x/'
module_path[] = '$APPROOT$/test/testUtils/conf/modules/module-test-foo/'
module_select[] = 'authent-local'
module_select[] = 'itop-config'
module_select[] = 'itop-config-mgmt'
module_select[] = 'itop-profiles-itil'
module_select[] = 'itop-welcome-itil'
module_select[] = 'module-test-foo'

View File

@@ -0,0 +1,20 @@
[main]
compileFrom = ''
baseConfig = '$APPROOT$/conf/production/config-itop.php'
module_path[] = '$APPROOT$/datamodels/2.x/authent-local/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-config/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-config-mgmt/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-profiles-itil/'
module_path[] = '$APPROOT$/datamodels/2.x/itop-welcome-itil/'
module_path[] = '$APPROOT$/test/testUtils/conf/modules/module-test-foo/'
module_select[] = 'authent-local'
module_select[] = 'itop-config'
module_select[] = 'itop-config-mgmt'
module_select[] = 'itop-profiles-itil'
module_select[] = 'itop-welcome-itil'
module_select[] = 'module-test-foo'