Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts: # test/sources/application/Helper/WebResourcesHelperTest.php # tests/php-unit-tests/ItopTestCase.php # tests/php-unit-tests/composer.lock # tests/php-unit-tests/unitary-tests/.make/release/DatamodelsXmlFilesTest.php # tests/php-unit-tests/unitary-tests/application/UI/Base/Layout/NavigationMenuTest.php # tests/php-unit-tests/unitary-tests/application/query/QueryTest.php # tests/php-unit-tests/unitary-tests/core/DBSearchTest.php # tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-config/BulkChangeExtKeyTest.inc.php # tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-config/CRUD/DBObjectTest.php # tests/php-unit-tests/unitary-tests/service/EventTest.php # tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_3.1.expected.xml # tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_3.1.input.xml # tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.0.expected.xml # tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.0.input.xml # tests/php-unit-tests/unitary-tests/sources/Application/Helper/WebResourcesHelperTest.php # tests/php-unit-tests/unitary-tests/sources/Router/RouterTest.php # tests/php-unit-tests/unitary-tests/sources/application/Helper/WebResourcesHelperTest.php # tests/php-unit-tests/unitary-tests/webservices/ImportTest.inc.php # tests/php-unit-tests/unitary-tests/webservices/RestTest.php
922
tests/php-unit-tests/ItopDataTestCase.php
Normal file
@@ -0,0 +1,922 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-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
|
||||
// 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 CMDBObject;
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
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;
|
||||
use XMLDataLoader;
|
||||
|
||||
|
||||
/** @see \Combodo\iTop\Test\UnitTest\ItopDataTestCase::CreateObjectWithTagSet() */
|
||||
define('TAG_CLASS', 'FAQ');
|
||||
define('TAG_ATTCODE', 'domains');
|
||||
|
||||
/**
|
||||
* Helper class to extend for tests needing access to iTop's metamodel
|
||||
*
|
||||
* **⚠ Warning** Each class extending this one needs to add the following annotations :
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4624 processIsolation is disabled by default and must be enabled in each test needing it (basically all tests using
|
||||
* iTop datamodel)
|
||||
*/
|
||||
class ItopDataTestCase extends ItopTestCase
|
||||
{
|
||||
private $iTestOrgId;
|
||||
|
||||
// For cleanup
|
||||
private $aCreatedObjects = array();
|
||||
|
||||
// Counts
|
||||
public $aReloadCount = [];
|
||||
|
||||
const USE_TRANSACTION = true;
|
||||
const CREATE_TEST_ORG = false;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('application/utils.inc.php');
|
||||
|
||||
$sEnv = 'production';
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
|
||||
|
||||
if (static::USE_TRANSACTION)
|
||||
{
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
if (static::CREATE_TEST_ORG)
|
||||
{
|
||||
$this->CreateTestOrganization();
|
||||
}
|
||||
|
||||
EventService::RegisterListener(EVENT_DB_OBJECT_RELOAD, [$this, 'CountObjectReload']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
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("Error when removing created objects: $sClass::$iKey. Exception message: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 \UserLocal
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function CreateUser($sLogin, $iProfileId, $sPassword=null, $iContactid=2)
|
||||
{
|
||||
$oUser = $this->CreateContactlessUser($sLogin, $iProfileId, $sPassword);
|
||||
$oUser->Set('contactid', $iContactid);
|
||||
$oUser->DBWrite();
|
||||
return $oUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sLogin
|
||||
* @param int $iProfileId
|
||||
*
|
||||
* @return \UserLocal
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function CreateContactlessUser($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);
|
||||
/** @var \UserLocal $oUser */
|
||||
$oUser = $this->createObject('UserLocal', array(
|
||||
'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 \ormLinkSet $oSet */
|
||||
$oSet = $oUser->Get('profile_list');
|
||||
$oSet->AddItem($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->assertArrayHasKey($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->assertArrayHasKey($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();
|
||||
return $oOrg;
|
||||
}
|
||||
|
||||
public function ResetReloadCount()
|
||||
{
|
||||
$this->aReloadCount = [];
|
||||
}
|
||||
|
||||
public function DebugReloadCount($sMsg, $bResetCount = true)
|
||||
{
|
||||
$iTotalCount = 0;
|
||||
$aTotalPerClass = [];
|
||||
foreach ($this->aReloadCount as $sClass => $aCountByKeys) {
|
||||
$iClassCount = 0;
|
||||
foreach ($aCountByKeys as $iCount) {
|
||||
$iClassCount += $iCount;
|
||||
}
|
||||
$iTotalCount += $iClassCount;
|
||||
$aTotalPerClass[$sClass] = $iClassCount;
|
||||
}
|
||||
$this->debug("$sMsg - $iTotalCount reload(s)");
|
||||
foreach ($this->aReloadCount as $sClass => $aCountByKeys) {
|
||||
$this->debug(" $sClass => $aTotalPerClass[$sClass] reload(s)");
|
||||
foreach ($aCountByKeys as $sKey => $iCount) {
|
||||
$this->debug(" $sClass::$sKey => $iCount");
|
||||
}
|
||||
}
|
||||
if ($bResetCount) {
|
||||
$this->ResetReloadCount();
|
||||
}
|
||||
}
|
||||
|
||||
public function CountObjectReload(EventData $oData)
|
||||
{
|
||||
$oObject = $oData->Get('object');
|
||||
$sClass = get_class($oObject);
|
||||
$sKey = $oObject->GetKey();
|
||||
$iCount = $this->GetObjectReloadCount($sClass, $sKey);
|
||||
$this->aReloadCount[$sClass][$sKey] = 1 + $iCount;
|
||||
}
|
||||
|
||||
public function GetObjectReloadCount($sClass, $sKey)
|
||||
{
|
||||
return $this->aReloadCount[$sClass][$sKey] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a series of operations will trigger a given number of MySL queries
|
||||
*
|
||||
* @param $iExpectedCount Number of MySQL queries that should be executed
|
||||
* @param callable $oFunction Operations to perform
|
||||
*
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLQueryHasNoResultException
|
||||
*/
|
||||
protected function assertDBQueryCount($iExpectedCount, callable $oFunction)
|
||||
{
|
||||
$iInitialCount = (int) CMDBSource::QueryToScalar("SHOW SESSION STATUS LIKE 'Queries'", 1);
|
||||
$oFunction();
|
||||
$iFinalCount = (int) CMDBSource::QueryToScalar("SHOW SESSION STATUS LIKE 'Queries'", 1);
|
||||
$iCount = $iFinalCount - 1 - $iInitialCount;
|
||||
if ($iCount != $iExpectedCount)
|
||||
{
|
||||
$this->fail("Expected $iExpectedCount queries. $iCount have been executed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, PHP Unit will consider that no assertion has been made
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a set of XML files describing a consistent set of iTop objects
|
||||
* @param string[] $aFiles
|
||||
* @param boolean $bSearch If true, a search will be performed on each object (based on its reconciliation keys)
|
||||
* before trying to import it (existing objects will be updated)
|
||||
* @return int Number of objects created
|
||||
*/
|
||||
protected function CreateFromXMLFiles($aFiles, $bSearch = false)
|
||||
{
|
||||
$oLoader = new XMLDataLoader();
|
||||
$oLoader->StartSession(CMDBObject::GetCurrentChange());
|
||||
foreach($aFiles as $sFilePath)
|
||||
{
|
||||
$oLoader->LoadFile($sFilePath, false, $bSearch);
|
||||
}
|
||||
$oLoader->EndSession();
|
||||
|
||||
return $oLoader->GetCountCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a consistent set of iTop objects from the specified XML text string
|
||||
* @param string $sXmlDataset
|
||||
* @param boolean $bSearch If true, a search will be performed on each object (based on its reconciliation keys)
|
||||
* before trying to import it (existing objects will be updated)
|
||||
* @return int The number of objects created
|
||||
*/
|
||||
protected function CreateFromXMLString($sXmlDataset, $bSearch = false)
|
||||
{
|
||||
$sTmpFileName = tempnam(sys_get_temp_dir(), 'xml');
|
||||
file_put_contents($sTmpFileName, $sXmlDataset);
|
||||
|
||||
$ret = $this->CreateFromXMLFiles([$sTmpFileName], $bSearch);
|
||||
|
||||
unlink($sTmpFileName);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
282
tests/php-unit-tests/ItopTestCase.php
Normal file
@@ -0,0 +1,282 @@
|
||||
<?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;
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 20/11/2017
|
||||
* Time: 11:21
|
||||
*/
|
||||
|
||||
use CMDBSource;
|
||||
use MySQLTransactionNotClosedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SetupUtils;
|
||||
|
||||
class ItopTestCase extends TestCase
|
||||
{
|
||||
const TEST_LOG_DIR = 'test';
|
||||
static $DEBUG_UNIT_TEST = false;
|
||||
|
||||
/** @noinspection UsingInclusionOnceReturnValueInspection avoid errors for approot includes */
|
||||
protected function setUp(): void
|
||||
{
|
||||
$sAppRootRelPath = 'approot.inc.php';
|
||||
$sDepthSeparator = '../';
|
||||
for ($iDepth = 0; $iDepth < 8; $iDepth++) {
|
||||
if (file_exists($sAppRootRelPath)) {
|
||||
require_once $sAppRootRelPath;
|
||||
break;
|
||||
}
|
||||
|
||||
$sAppRootRelPath = $sDepthSeparator.$sAppRootRelPath;
|
||||
}
|
||||
|
||||
static::$DEBUG_UNIT_TEST = getenv('DEBUG_UNIT_TEST');
|
||||
|
||||
$this->debug("\n----------\n---------- ".$this->getName()."\n----------\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \MySQLTransactionNotClosedException see N°5538
|
||||
* @since 2.7.8 3.0.3 3.1.0 N°5538
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
if (CMDBSource::IsInsideTransaction()) {
|
||||
// Nested transactions were opened but not finished !
|
||||
throw new MySQLTransactionNotClosedException('Some DB transactions were opened but not closed ! Fix the code by adding ROLLBACK or COMMIT statements !', []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Require once an iTop file (core or extension) from its relative path to the iTop root dir.
|
||||
* This ensure to always use the right absolute path, especially in {@see \Combodo\iTop\Test\UnitTest\ItopTestCase::RequireOnceUnitTestFile()}
|
||||
*
|
||||
* @param string $sFileRelPath Rel. path (from iTop root dir) of the iTop file (core or extension) to require (eg. 'core/attributedef.class.inc.php' for <ITOP>/core/attributedef.class.inc.php)
|
||||
*
|
||||
* @return void
|
||||
* @since 2.7.9 3.0.3 3.1.0 N°5608 Add method after PHPUnit directory moving
|
||||
*/
|
||||
protected function RequireOnceItopFile(string $sFileRelPath): void
|
||||
{
|
||||
require_once APPROOT . $sFileRelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require once a unit test file (eg. a mock class) from its relative path from the *current* dir.
|
||||
* This ensure that required files don't crash when unit tests dir is moved in the iTop structure (see N°5608)
|
||||
*
|
||||
* @param string $sFileRelPath Rel. path (from the *current* dir) of the unit test file to require (eg. './WeeklyScheduledProcessMockConfig.php' for <ITOP>/tests/php-unit-tests/unitary-tests/core/WeeklyScheduledProcessMockConfig.php in Combodo\iTop\Test\UnitTest\Core\WeeklyScheduledProcessTest)
|
||||
*
|
||||
* @return void
|
||||
* @since 2.7.9 3.0.3 3.1.0 N°5608 Add method after PHPUnit directory moving
|
||||
*/
|
||||
protected function RequireOnceUnitTestFile(string $sFileRelPath): void
|
||||
{
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$sCallerDirAbsPath = dirname($aStack[0]['file']);
|
||||
|
||||
require_once $sCallerDirAbsPath . DIRECTORY_SEPARATOR . $sFileRelPath;
|
||||
}
|
||||
|
||||
protected function debug($sMsg)
|
||||
{
|
||||
if (static::$DEBUG_UNIT_TEST) {
|
||||
if (is_string($sMsg)) {
|
||||
echo "$sMsg\n";
|
||||
} else {
|
||||
/** @noinspection ForgottenDebugOutputInspection */
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.4 3.0.0
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 2.7.4 3.0.0
|
||||
*/
|
||||
public function InvokeNonPublicMethod($sObjectClass, $sMethodName, $oObject, $aArgs)
|
||||
{
|
||||
$class = new \ReflectionClass($sObjectClass);
|
||||
$method = $class->getMethod($sMethodName);
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invokeArgs($oObject, $aArgs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param object $oObject
|
||||
* @param string $sProperty
|
||||
*
|
||||
* @return mixed property
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
* @since 2.7.8 3.0.3 3.1.0
|
||||
*/
|
||||
public function GetNonPublicProperty(object $oObject, string $sProperty)
|
||||
{
|
||||
$class = new \ReflectionClass(get_class($oObject));
|
||||
$property = $class->getProperty($sProperty);
|
||||
$property->setAccessible(true);
|
||||
|
||||
return $property->getValue($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $oObject
|
||||
* @param string $sProperty
|
||||
* @param $value
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
* @since 2.7.8 3.0.3 3.1.0
|
||||
*/
|
||||
public function SetNonPublicProperty(object $oObject, string $sProperty, $value)
|
||||
{
|
||||
$class = new \ReflectionClass(get_class($oObject));
|
||||
$property = $class->getProperty($sProperty);
|
||||
$property->setAccessible(true);
|
||||
|
||||
$property->setValue($oObject, $value);
|
||||
}
|
||||
|
||||
public function RecurseRmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (is_dir($dir.DIRECTORY_SEPARATOR.$object))
|
||||
$this->RecurseRmdir($dir.DIRECTORY_SEPARATOR.$object);
|
||||
else
|
||||
unlink($dir.DIRECTORY_SEPARATOR.$object);
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateTmpdir() {
|
||||
$sTmpDir=tempnam(sys_get_temp_dir(),'');
|
||||
if (file_exists($sTmpDir))
|
||||
{
|
||||
unlink($sTmpDir);
|
||||
}
|
||||
mkdir($sTmpDir);
|
||||
if (is_dir($sTmpDir))
|
||||
{
|
||||
return $sTmpDir;
|
||||
}
|
||||
|
||||
return sys_get_temp_dir();
|
||||
}
|
||||
|
||||
public function RecurseMkdir($sDir){
|
||||
if (strpos($sDir, DIRECTORY_SEPARATOR) === 0){
|
||||
$sPath = DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$sPath = "";
|
||||
}
|
||||
|
||||
foreach (explode(DIRECTORY_SEPARATOR, $sDir) as $sSubDir){
|
||||
if (($sSubDir === '..')) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (( trim($sSubDir) === '' ) || ( $sSubDir === '.' )) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sPath .= $sSubDir . DIRECTORY_SEPARATOR;
|
||||
if (!is_dir($sPath)) {
|
||||
var_dump($sPath);
|
||||
@mkdir($sPath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function RecurseCopy($src,$dst) {
|
||||
$dir = opendir($src);
|
||||
@mkdir($dst);
|
||||
while(false !== ( $file = readdir($dir)) ) {
|
||||
if (( $file != '.' ) && ( $file != '..' )) {
|
||||
if ( is_dir($src . '/' . $file) ) {
|
||||
$this->RecurseCopy($src . DIRECTORY_SEPARATOR . $file,$dst . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
else {
|
||||
copy($src . DIRECTORY_SEPARATOR . $file,$dst . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
}
|
||||
7
tests/php-unit-tests/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# PHP unitary tests
|
||||
## Where should I add my test?
|
||||
|
||||
- Covers an iTop PHP class or method?
|
||||
- Most likely in "unitary-tests".
|
||||
- Covers the consistency of some data through the app?
|
||||
- Most likely in "integration-tests".
|
||||
6
tests/php-unit-tests/composer.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit" : "^9",
|
||||
"sempro/phpunit-pretty-print": "^1.4"
|
||||
}
|
||||
}
|
||||
1787
tests/php-unit-tests/composer.lock
generated
Normal file
@@ -0,0 +1,214 @@
|
||||
<?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\Integration;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use Dict;
|
||||
|
||||
class DictionariesConsistencyTest extends ItopTestCase
|
||||
{
|
||||
/**
|
||||
* Verify that language declarations match the file names (same language codes)
|
||||
*
|
||||
* @dataProvider DictionaryFileProvider
|
||||
*
|
||||
* @param $sDictFile
|
||||
*/
|
||||
public function testDictionariesLanguage($sDictFile): void
|
||||
{
|
||||
$aPrefixToLanguageData = array(
|
||||
'cs' => array('CS CZ', 'Czech', 'Čeština'),
|
||||
'da' => array('DA DA', 'Danish', 'Dansk'),
|
||||
'de' => array('DE DE', 'German', 'Deutsch'),
|
||||
'en' => array('EN US', 'English', 'English'),
|
||||
'es_cr' => array('ES CR', 'Spanish', array(
|
||||
'Español, Castellaño', // old value
|
||||
'Español, Castellano', // new value since N°3635
|
||||
)),
|
||||
'fr' => array('FR FR', 'French', 'Français'),
|
||||
'hu' => array('HU HU', 'Hungarian', 'Magyar'),
|
||||
'it' => array('IT IT', 'Italian', 'Italiano'),
|
||||
'ja' => array('JA JP', 'Japanese', '日本語'),
|
||||
'nl' => array('NL NL', 'Dutch', 'Nederlands'),
|
||||
'pl' => array('PL PL', 'Polish', 'Polski'),
|
||||
'pt_br' => array('PT BR', 'Brazilian', 'Brazilian'),
|
||||
'ru' => array('RU RU', 'Russian', 'Русский'),
|
||||
'sk' => array('SK SK', 'Slovak', 'Slovenčina'),
|
||||
'tr' => array('TR TR', 'Turkish', 'Türkçe'),
|
||||
'zh_cn' => array('ZH CN', 'Chinese', '简体中文'),
|
||||
);
|
||||
|
||||
if (!preg_match('/^(.*)\\.dict/', basename($sDictFile), $aMatches)) {
|
||||
static::fail("Dictionary file '$sDictFile' not matching the naming convention");
|
||||
}
|
||||
|
||||
$sLangPrefix = $aMatches[1];
|
||||
if (!array_key_exists($sLangPrefix, $aPrefixToLanguageData)) {
|
||||
static::fail("Unknown prefix '$sLangPrefix' for dictionary file '$sDictFile'");
|
||||
}
|
||||
|
||||
[$sExpectedLanguageCode, $sExpectedEnglishLanguageDesc, $aExpectedLocalizedLanguageDesc] = $aPrefixToLanguageData[$sLangPrefix];
|
||||
|
||||
$sDictPHP = file_get_contents($sDictFile);
|
||||
$iCount = preg_match_all("@Dict::Add\('(.*)'\s*,\s*'(.*)'\s*,\s*'(.*)'@", $sDictPHP, $aMatches);
|
||||
if ($iCount === false) {
|
||||
static::fail("Pattern not working");
|
||||
}
|
||||
if ($iCount === 0) {
|
||||
// Empty dictionary, that's fine!
|
||||
static::assertTrue(true);
|
||||
}
|
||||
foreach ($aMatches[1] as $sLanguageCode) {
|
||||
static::assertSame($sExpectedLanguageCode, $sLanguageCode,
|
||||
"Unexpected language code for Dict::Add in dictionary $sDictFile");
|
||||
}
|
||||
foreach ($aMatches[2] as $sEnglishLanguageDesc) {
|
||||
static::assertSame($sExpectedEnglishLanguageDesc, $sEnglishLanguageDesc,
|
||||
"Unexpected language description (english) for Dict::Add in dictionary $sDictFile");
|
||||
}
|
||||
foreach ($aMatches[3] as $sLocalizedLanguageDesc)
|
||||
{
|
||||
if (false === is_array($aExpectedLocalizedLanguageDesc)) {
|
||||
$aExpectedLocalizedLanguageDesc = array($aExpectedLocalizedLanguageDesc);
|
||||
}
|
||||
static::assertContains($sLocalizedLanguageDesc,$aExpectedLocalizedLanguageDesc,
|
||||
"Unexpected language description for Dict::Add in dictionary $sDictFile");
|
||||
}
|
||||
}
|
||||
|
||||
public function DictionaryFileProvider(): array
|
||||
{
|
||||
$this->setUp();
|
||||
|
||||
$aDictFiles = array_merge(
|
||||
glob(APPROOT.'datamodels/2.x/*/*.dict*.php'), // legacy form in modules
|
||||
glob(APPROOT.'datamodels/2.x/*/dictionaries/*.dict*.php'), // modern form in modules
|
||||
glob(APPROOT.'dictionaries/*.dict*.php') // framework
|
||||
);
|
||||
$aTestCases = array();
|
||||
foreach ($aDictFiles as $sDictFile) {
|
||||
$aTestCases[$sDictFile] = array('sDictFile' => $sDictFile);
|
||||
}
|
||||
|
||||
return $aTestCases;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider DictionaryFileProvider
|
||||
*
|
||||
* @param string $sDictFile
|
||||
*
|
||||
* @group beforeSetup
|
||||
*
|
||||
* @uses CheckDictionarySyntax
|
||||
*/
|
||||
public function testStandardDictionariesPhpSyntax(string $sDictFile): void
|
||||
{
|
||||
$this->CheckDictionarySyntax($sDictFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that {@see CheckDictionarySyntax} works as expected by passing 2 test dictionaries
|
||||
*
|
||||
* @uses CheckDictionarySyntax
|
||||
*/
|
||||
public function testPlaygroundDictionariesPhpSyntax(): void
|
||||
{
|
||||
$this->CheckDictionarySyntax(__DIR__.'/dictionaries-test/fr.dictionary.itop.core.KO.wrong_php', false);
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$this->CheckDictionarySyntax(__DIR__.'/dictionaries-test/fr.dictionary.itop.core.OK.php', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDictFile complete path for the file to check
|
||||
* @param bool $bIsSyntaxValid expected assert value
|
||||
*
|
||||
* @uses `php -l`
|
||||
* @uses \assertEquals()
|
||||
*/
|
||||
private function CheckDictionarySyntax(string $sDictFile, $bIsSyntaxValid = true): void
|
||||
{
|
||||
exec("php -l {$sDictFile}", $output, $return);
|
||||
|
||||
$bDictFileSyntaxOk = ($return === 0);
|
||||
|
||||
$sMessage = "File `{$sDictFile}` syntax didn't matched expectations\nparsing results=".var_export($output, true);
|
||||
self::assertEquals($bIsSyntaxValid, $bDictFileSyntaxOk, $sMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ImportCsvMessageStillOkProvider
|
||||
* make sure N°5305 dictionary changes are still here and UI remains unbroken for any lang
|
||||
*/
|
||||
public function testImportCsvMessageStillOk($sLangCode, $sDictFile)
|
||||
{
|
||||
$aFailedLabels = [];
|
||||
$aLabelsToTest = [
|
||||
'UI:CSVReport-Value-SetIssue' => [],
|
||||
'UI:CSVReport-Value-ChangeIssue' => [ 'arg1' ],
|
||||
'UI:CSVReport-Value-NoMatch' => [ 'arg1' ],
|
||||
'UI:CSVReport-Value-NoMatch-PossibleValues' => [ 'arg1', 'arg2' ],
|
||||
'UI:CSVReport-Value-NoMatch-NoObject' => [ 'arg1' ],
|
||||
'UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser' => [ 'arg1' ],
|
||||
'UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser' => [ 'arg1' ],
|
||||
];
|
||||
|
||||
$sLanguageCode = strtoupper(str_replace('-', ' ', $sLangCode));
|
||||
require_once(APPROOT.'env-'.\utils::GetCurrentEnvironment().'/dictionaries/languages.php');
|
||||
Dict::SetUserLanguage($sLanguageCode);
|
||||
foreach ($aLabelsToTest as $sLabelKey => $aLabelArgs){
|
||||
try{
|
||||
$sLabelValue = Dict::Format($sLabelKey, ...$aLabelArgs);
|
||||
//$this->debug($sLabelValue);
|
||||
} catch (\ValueError $e){
|
||||
$aFailedLabels[] = $sLabelKey;
|
||||
|
||||
$this->debug([
|
||||
'exception' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
'label_name' => $sLabelKey,
|
||||
'label_args' =>$aLabelArgs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->assertEquals([], $aFailedLabels, "test fail for lang $sLangCode and labels (" . implode(", ", $aFailedLabels) . ')');
|
||||
}
|
||||
|
||||
public function ImportCsvMessageStillOkProvider(){
|
||||
return $this->GetDictFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* return a map linked to *.dict.php files that are generated after setup
|
||||
* each entry key is lang code (example 'en')
|
||||
* each value is an array with lang code (again) and dict file path
|
||||
* @return array
|
||||
*/
|
||||
private function GetDictFiles() : array {
|
||||
$aDictFiles = [];
|
||||
|
||||
foreach (glob(APPROOT.'env-'.\utils::GetCurrentEnvironment().'/dictionaries/*.dict.php') as $sDictFile){
|
||||
if (preg_match('/.*\\/(.*).dict.php/', $sDictFile, $aMatches)){
|
||||
$sLangCode = $aMatches[1];
|
||||
$aDictFiles[$sLangCode] = [
|
||||
'lang' => $sLangCode,
|
||||
'file' => $sDictFile
|
||||
];
|
||||
}
|
||||
}
|
||||
return $aDictFiles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* @used-by \Combodo\iTop\Test\UnitTest\Integration\DictionariesConsistencyTest::testPlaygroundDictionariesPhpSyntax
|
||||
*
|
||||
* This is a PHP file containing invalid code.
|
||||
* The file extension was changed so that it won't display an error in the IDE.
|
||||
* Didn't find any other way to disable syntax alerts (like @noinspection)
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'MyDictKey1' => 'l'échappement : mauvais exemple 1',
|
||||
'MyDictKey2' => 'l''échappement : mauvais exemple 2',
|
||||
'MyDictKey3' => 'l/'échappement : mauvais exemple 3',
|
||||
));
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* @used-by \Combodo\iTop\Test\UnitTest\Integration\DictionariesConsistencyTest::testPlaygroundDictionariesPhpSyntax
|
||||
*/
|
||||
|
||||
Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'MyDictKey1' => 'l\'échappement : bon exemple 1',
|
||||
'MyDictKey2' => 'l\'échappement : bon exemple 2',
|
||||
'MyDictKey3' => 'l\'échappement : bon exemple 3',
|
||||
));
|
||||
@@ -0,0 +1,113 @@
|
||||
<?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\Integration;
|
||||
|
||||
use ApplicationException;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use utils;
|
||||
|
||||
|
||||
/**
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModulesPhpVersionIntegrationTest extends ItopTestCase {
|
||||
/**
|
||||
* @param string $sPhpFile iTop module file
|
||||
*
|
||||
* @return string module version
|
||||
*/
|
||||
private function GetItopModuleVersion(string $sPhpFile): ?string {
|
||||
$sModulePath = realpath($sPhpFile);
|
||||
$sModuleFileName = basename($sModulePath);
|
||||
$sModuleName = preg_replace('/[^.]+\.([^.]+)\.php/', '$1', $sModuleFileName);
|
||||
|
||||
$sFileContent = file_get_contents($sPhpFile);
|
||||
|
||||
preg_match(
|
||||
"#'$sModuleName/([^']+)'#",
|
||||
$sFileContent,
|
||||
$matches
|
||||
);
|
||||
|
||||
return $matches[1] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if `module.*.php` files contained in `datamodels/1.x` or `datamodels/2.x` refers to the current itop version
|
||||
* This is an integration test
|
||||
*
|
||||
* As ess and pro targets are copying modules into datamodels/2.x this test can only be run on a community target !
|
||||
*
|
||||
* @group itop-community
|
||||
* @group skipPostBuild
|
||||
*
|
||||
* @uses utils::GetItopMinorVersion()
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4714 uses new {@link ITOP_CORE_VERSION} constant
|
||||
* @since 3.0.3 3.1.0 move itop-community group in this method
|
||||
*/
|
||||
public function testITopModulesPhpVersion(): void {
|
||||
if (is_dir(APPROOT.'datamodels/2.x')) {
|
||||
$DatamodelsPath = APPROOT.'datamodels/2.x';
|
||||
} elseif (is_dir(APPROOT.'datamodels/1.x')) {
|
||||
$DatamodelsPath = APPROOT.'datamodels/1.x';
|
||||
} else {
|
||||
throw new \Exception('Cannot local the datamodels directory');
|
||||
}
|
||||
|
||||
$this->RequireOnceItopFile('core/config.class.inc.php');
|
||||
$sPath = $DatamodelsPath.'/*/module.*.php';
|
||||
$aPhpFiles = glob($sPath);
|
||||
|
||||
$sExpectedVersion = ITOP_CORE_VERSION;
|
||||
|
||||
$aModuleWithError = [];
|
||||
foreach ($aPhpFiles as $sPhpFile) {
|
||||
$sActualVersion = $this->GetItopModuleVersion($sPhpFile);
|
||||
|
||||
$this->assertSame($sExpectedVersion, $sActualVersion,
|
||||
'Module desc file does not contain the same version as the core: '.$sPhpFile);
|
||||
}
|
||||
|
||||
self::assertEquals([], $aModuleWithError, 'Some modules have wrong versions ! They should match '.$sExpectedVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ItopWikiVersionProvider
|
||||
* @since 2.7.7 3.0.1 3.1.1 N°4714 new ITOP_CORE_VERSION constant
|
||||
*/
|
||||
public function testItopWikiVersion($sItopVersion, $sExpectedWikiVersion) {
|
||||
try {
|
||||
$sActualWikiVersion = utils::GetItopVersionWikiSyntax($sItopVersion);
|
||||
}
|
||||
catch (ApplicationException $e) {
|
||||
self::fail('Cannot get wiki version : '.$e->getMessage());
|
||||
}
|
||||
self::assertSame($sExpectedWikiVersion, $sActualWikiVersion, 'Computed wiki version is wrong !');
|
||||
}
|
||||
|
||||
public function ItopWikiVersionProvider()
|
||||
{
|
||||
return [
|
||||
['2.7.0', '2_7_0'],
|
||||
['2.7.7', '2_7_0'],
|
||||
['3.0.0', '3_0_0'],
|
||||
['3.0.1', '3_0_0'],
|
||||
['3.1.0', '3_1_0'],
|
||||
['3.1.1', '3_1_0'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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\Integration;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use DOMDocument;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModulesXmlVersionIntegrationTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->RequireOnceItopFile('setup/itopdesignformat.class.inc.php');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify if the `datamodels/2.x/datamodel.*.xml` files refer to the latest version of the design
|
||||
* This is an integration test
|
||||
*
|
||||
* As ess and pro targets are copying modules into datamodels/2.x this test can only be run on a community target !
|
||||
*
|
||||
* @group itop-community
|
||||
* @group skipPostBuild
|
||||
*
|
||||
* @dataProvider DatamodelItopXmlVersionProvider
|
||||
*
|
||||
* @since 3.0.3 3.1.0 move itop-community group in this method
|
||||
*/
|
||||
public function testDatamodelItopXmlVersion($sXmlFile)
|
||||
{
|
||||
$oOriginalXml = new DOMDocument();
|
||||
$oOriginalXml->load($sXmlFile);
|
||||
|
||||
$oTransformedXml = new DOMDocument();
|
||||
$oTransformedXml->load($sXmlFile);
|
||||
$oFormat = new iTopDesignFormat($oTransformedXml);
|
||||
|
||||
if ($oFormat->Convert()) {
|
||||
// Compare the original and new format
|
||||
$sExpectedXmlVersion = ITOP_DESIGN_LATEST_VERSION;
|
||||
$this->assertSame($oTransformedXml->saveXML(), $oOriginalXml->saveXML(),
|
||||
"Datamodel file $sXmlFile:2 not in the latest format ($sExpectedXmlVersion)");
|
||||
} else {
|
||||
$this->fail("Failed to convert $sXmlFile into the latest format");
|
||||
}
|
||||
}
|
||||
|
||||
public function DatamodelItopXmlVersionProvider()
|
||||
{
|
||||
static::setUp();
|
||||
|
||||
$sPath = APPROOT.'datamodels/2.x/*/datamodel.*.xml';
|
||||
$aXmlFiles = glob($sPath);
|
||||
|
||||
$aXmlFiles[] = APPROOT.'core/datamodel.core.xml';
|
||||
$aXmlFiles[] = APPROOT.'application/datamodel.application.xml';
|
||||
|
||||
$aTestCases = array();
|
||||
foreach ($aXmlFiles as $sXmlFile) {
|
||||
$aTestCases[$sXmlFile] = array(
|
||||
'sXmlFile' => $sXmlFile,
|
||||
);
|
||||
}
|
||||
|
||||
return $aTestCases;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?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\Integration;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
|
||||
/**
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopXmlVersionIntegrationTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->RequireOnceItopFile('core/config.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/itopdesignformat.class.inc.php');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify if the latest version of the XML datamodel is aligned with the app. core version
|
||||
* This is an integration test
|
||||
*
|
||||
* @group skipPostBuild
|
||||
*/
|
||||
public function testItopXmlVersion()
|
||||
{
|
||||
// Retrieve only first 2 parts of the version
|
||||
$aCoreVersionParts = explode('.', ITOP_CORE_VERSION);
|
||||
$sCoreVersion = $aCoreVersionParts[0].'.'.$aCoreVersionParts[1];
|
||||
|
||||
$sXMLVersion = ITOP_DESIGN_LATEST_VERSION;
|
||||
$this->assertSame($sXMLVersion, $sCoreVersion, "XML datamodel version (ITOP_DESIGN_LATEST_VERSION={$sXMLVersion}) is not aligned with the app. core version (ITOP_CORE_VERSION={$sCoreVersion})");
|
||||
}
|
||||
}
|
||||
342
tests/php-unit-tests/legacy-tests/GroupByAndFunctions.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?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 ('../../../approot.inc.php');
|
||||
require_once(APPROOT.'application/application.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
require_once(APPROOT.'application/loginwebpage.class.inc.php');
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Main program
|
||||
//
|
||||
LoginWebPage::DoLogin(true); // Check user rights and prompt if needed
|
||||
|
||||
|
||||
$sSubmit = utils::ReadParam('submit', '', false, 'raw_data');
|
||||
if ($sSubmit != 'Reset')
|
||||
{
|
||||
$sOQL = utils::ReadParam('OQL_Request', '', false, 'raw_data');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOQL = '';
|
||||
}
|
||||
$bError = false;
|
||||
$oP = new iTopWebPage('Database inconsistencies');
|
||||
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'tests/');
|
||||
$oP->set_title('Grouping with functions');
|
||||
$oP->add('<div style="padding: 15px;"><h2>Grouping with functions</h2>');
|
||||
$oP->add('<div style="padding: 15px; background: #ddd;">');
|
||||
try
|
||||
{
|
||||
if (!empty($sOQL))
|
||||
{
|
||||
// Getting class attributes
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
$aSearches = $oSearch->GetSearches();
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
$sClass = $aSearches[0]->GetClassAlias();
|
||||
$sRealClass = $aSearches[0]->GetClass();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClass = $oSearch->GetClassAlias();
|
||||
$sRealClass = $oSearch->GetClass();
|
||||
}
|
||||
|
||||
$sGroupBy1 = utils::ReadParam('groupby_1', '');
|
||||
$sGroupBy2 = utils::ReadParam('groupby_2', '');
|
||||
$sOrderBy1 = utils::ReadParam('orderby_1', '');
|
||||
$sOrderBy2 = utils::ReadParam('orderby_2', '');
|
||||
|
||||
$sAttributesOptions1 = '';
|
||||
$sAttributesOptions2 = '';
|
||||
$sAttributesOptions3 = '';
|
||||
$sAttributesOptions4 = '';
|
||||
|
||||
foreach(array('_itop_sum_', '_itop_avg_', '_itop_min_', '_itop_max_', '_itop_count_', 'group1', 'group2') as $sAttCode)
|
||||
{
|
||||
$sAttributesOptions3 .= '<option value="'.$sAttCode.'" '.($sOrderBy1 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
$sAttributesOptions4 .= '<option value="'.$sAttCode.'" '.($sOrderBy2 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
}
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sRealClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
foreach($aSearches as $oSubQuery)
|
||||
{
|
||||
$sSubClass = $oSubQuery->GetClass();
|
||||
if (!MetaModel::IsValidAttCode($sSubClass, $sAttCode))
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sAttributesOptions1 .= '<option value="'.$sAttCode.'" '.($sGroupBy1 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
$sAttributesOptions2 .= '<option value="'.$sAttCode.'" '.($sGroupBy2 == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
}
|
||||
|
||||
$iLimit = intval(utils::ReadParam('top', '0'));
|
||||
|
||||
$sInvOrder1 = utils::ReadParam('desc1', '');
|
||||
$sCheck1 = ($sInvOrder1 == 'on' ? 'checked' : '');
|
||||
|
||||
$sInvOrder2 = utils::ReadParam('desc2', '');
|
||||
$sCheck2 = ($sInvOrder2 == 'on' ? 'checked' : '');
|
||||
|
||||
$sFuncField = utils::ReadParam('funcfield', '');
|
||||
|
||||
$sFuncFieldOption = '';
|
||||
foreach(MetaModel::ListAttributeDefs($sRealClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if ($oSearch instanceof DBUnionSearch)
|
||||
{
|
||||
foreach($aSearches as $oSubQuery)
|
||||
{
|
||||
$sSubClass = $oSubQuery->GetClass();
|
||||
if (!MetaModel::IsValidAttCode($sSubClass, $sAttCode))
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (get_class($oAttDef))
|
||||
{
|
||||
case 'Integer':
|
||||
case 'AttributeDecimal':
|
||||
case 'AttributeDuration':
|
||||
case 'AttributeSubItem':
|
||||
case 'AttributePercentage':
|
||||
$sFuncFieldOption .= '<option value="'.$sAttCode.'" '.($sFuncField == $sAttCode ? 'selected' : '').'>'.$sAttCode.'</option>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
$bError = true;
|
||||
}
|
||||
$oP->add("<div><form>");
|
||||
$oP->add("<input type=\"submit\" name=\"submit\" value=\"Reset\">\n");
|
||||
$oP->add("</form></div>");
|
||||
|
||||
$oP->add("<form>");
|
||||
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
<div>
|
||||
<label>Search OQL:</label>
|
||||
<div>
|
||||
<textarea id='OQL_Request' name='OQL_Request' cols='60' rows='5'>$sOQL</textarea>
|
||||
</div>
|
||||
</div>
|
||||
EOF
|
||||
);
|
||||
|
||||
if (!empty($sOQL) && !$bError)
|
||||
{
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
<div>
|
||||
<label>Group by:</label>
|
||||
<div>
|
||||
<select id="groupby_1" name="groupby_1">
|
||||
$sAttributesOptions1
|
||||
</select>
|
||||
<select id="groupby_2" name="groupby_2">
|
||||
<option></option>
|
||||
$sAttributesOptions2
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Order by:</label>
|
||||
<div>
|
||||
<select id="orderby_1" name="orderby_1">$sAttributesOptions3</select>
|
||||
<label>Inv order</label><input type="checkbox" name="desc1" $sCheck1/>
|
||||
</div>
|
||||
<div>
|
||||
<select id="orderby_2" name="orderby_2">
|
||||
<option></option>
|
||||
$sAttributesOptions4
|
||||
</select>
|
||||
<label>Inv order</label><input type="checkbox" name="desc2" $sCheck2/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Functions on:</label>
|
||||
<div>
|
||||
<select id="funcfield" name="funcfield">$sFuncFieldOption</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Top:</label>
|
||||
<div><input type="text" id="top" name="top" value="$iLimit"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$oP->add("<input type=\"submit\" name=\"submit\" value=\"Search\">\n");
|
||||
|
||||
$oP->add("</form>");
|
||||
|
||||
$sSQL = '';
|
||||
|
||||
|
||||
if (empty($sOQL) || empty($sGroupBy1))
|
||||
{
|
||||
$oP->output();
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
$iLimitStart = 0;
|
||||
$aOrderBy = array();
|
||||
if (!empty($sOrderBy1))
|
||||
{
|
||||
$aOrderBy[$sOrderBy1] = ($sInvOrder1 != 'on');
|
||||
}
|
||||
if (!empty($sOrderBy2))
|
||||
{
|
||||
$aOrderBy[$sOrderBy2] = ($sInvOrder2 != 'on');
|
||||
}
|
||||
|
||||
$aGroupBy = array();
|
||||
$oExpr1 = Expression::FromOQL($sClass.'.'.$sGroupBy1);
|
||||
$aGroupBy["group1"] = $oExpr1;
|
||||
|
||||
if (!empty($sGroupBy2))
|
||||
{
|
||||
$oExpr2 = Expression::FromOQL($sClass.'.'.$sGroupBy2);
|
||||
$aGroupBy["group2"] = $oExpr2;
|
||||
}
|
||||
|
||||
$aArgs = array();
|
||||
|
||||
if (empty($sFuncField))
|
||||
{
|
||||
$aFunctions = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oTimeExpr = Expression::FromOQL($sClass.'.'.$sFuncField);
|
||||
$oSumExpr = new FunctionExpression('SUM', array($oTimeExpr));
|
||||
$oAvgExpr = new FunctionExpression('AVG', array($oTimeExpr));
|
||||
$oMinExpr = new FunctionExpression('MIN', array($oTimeExpr));
|
||||
$oMaxExpr = new FunctionExpression('MAX', array($oTimeExpr));
|
||||
// Alias => Expression
|
||||
$aFunctions = array(
|
||||
'_itop_sum_' => $oSumExpr,
|
||||
'_itop_avg_' => $oAvgExpr,
|
||||
'_itop_min_' => $oMinExpr,
|
||||
'_itop_max_' => $oMaxExpr,
|
||||
);
|
||||
}
|
||||
|
||||
$sSQL = $oSearch->MakeGroupByQuery($aArgs, $aGroupBy, false, $aFunctions, $aOrderBy, $iLimit, $iLimitStart);
|
||||
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
|
||||
// Display results
|
||||
if (!empty($aRes))
|
||||
{
|
||||
$oP->add('<div>');
|
||||
$oP->add('<table class="listResults">');
|
||||
$aLine = $aRes[0];
|
||||
$aCols = array();
|
||||
$oP->add('<tr>');
|
||||
foreach(array_keys($aLine) as $item)
|
||||
{
|
||||
if (!is_numeric($item))
|
||||
{
|
||||
$aCols[] = $item;
|
||||
$oP->add("<th>$item</th>");
|
||||
}
|
||||
}
|
||||
$oP->add('</tr>');
|
||||
|
||||
foreach($aRes as $aLine)
|
||||
{
|
||||
$oP->add('<tr>');
|
||||
foreach($aCols as $sCol)
|
||||
{
|
||||
$oP->add("<td>".$aLine[$sCol]."</td>");
|
||||
}
|
||||
$oP->add('</tr>');
|
||||
}
|
||||
|
||||
$oP->add('</table>');
|
||||
$oP->add('</div>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->add("<p>No Result</p>\n");
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oP->p('<div class="header_message message_error">'.$e->getMessage().'</div>');
|
||||
$bError = true;
|
||||
}
|
||||
|
||||
$oP->add("<div class=\"header_message message_info\">$sSQL</div>\n");
|
||||
|
||||
$oP->output();
|
||||
|
||||
return;
|
||||
|
||||
/*
|
||||
echo "<pre>";
|
||||
$aClassSelection = MetaModel::GetClasses();
|
||||
foreach($aClassSelection as $sClass)
|
||||
{
|
||||
if (!MetaModel::HasTable($sClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if (!MetaModel::IsAttributeOrigin($sClass, $sAttCode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (get_class($oAttDef))
|
||||
{
|
||||
case 'Integer':
|
||||
case 'AttributeDecimal':
|
||||
case 'AttributeDuration':
|
||||
case 'AttributeSubItem':
|
||||
case 'AttributePercentage':
|
||||
echo "$sClass:$sAttCode = ".get_class($oAttDef)."\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
1
tests/php-unit-tests/legacy-tests/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Tests in this folder have been written before the introduction of PHPUnit in iTop, they are not run by the CI.
|
||||
284
tests/php-unit-tests/legacy-tests/VerifyOQL.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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('../../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
|
||||
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
|
||||
ApplicationMenu::CheckMenuIdEnabled('RunQueriesMenu');
|
||||
|
||||
function ShowExamples($oP, $sExpression)
|
||||
{
|
||||
$bUsingExample = false;
|
||||
|
||||
$aExamples = array(
|
||||
'Pedagogic examples' => array(
|
||||
"Web applications" => "SELECT WebApplication",
|
||||
"Person having an 'A' in their name" => "SELECT Person AS B WHERE B.name LIKE '%A%'",
|
||||
"Servers having a name like dbserver1.demo.com or dbserver023.foo.fr" => "SELECT Server WHERE name REGEXP '^dbserver[0-9]+\\\\..+\\\\.[a-z]{2,3}$'",
|
||||
"Changes planned on new year's day" => "SELECT Change AS ch WHERE ch.start_date >= '2009-12-31' AND ch.end_date <= '2010-01-01'",
|
||||
"IPs in a range" => "SELECT DatacenterDevice AS dev WHERE INET_ATON(dev.managementip) > INET_ATON('10.22.32.224') AND INET_ATON(dev.managementip) < INET_ATON('10.22.32.255')",
|
||||
"Persons below a given root organization" => "SELECT Person AS P JOIN Organization AS Node ON P.org_id = Node.id JOIN Organization AS Root ON Node.parent_id BELOW Root.id WHERE Root.id=1",
|
||||
),
|
||||
'Usefull examples' => array(
|
||||
"NW interfaces of equipment in production for customer 'Demo'" => "SELECT PhysicalInterface AS if JOIN DatacenterDevice AS dev ON if.connectableci_id = dev.id WHERE dev.status = 'production' AND dev.organization_name = 'Demo'",
|
||||
"My tickets" => "SELECT Ticket AS t WHERE t.agent_id = :current_contact_id",
|
||||
"People being owner of an active ticket" => "SELECT Person AS p JOIN UserRequest AS u ON u.agent_id = p.id WHERE u.status != 'closed'",
|
||||
"Contracts terminating in the next thirty days" => "SELECT Contract AS c WHERE c.end_date > NOW() AND c.end_date < DATE_ADD(NOW(), INTERVAL 30 DAY)",
|
||||
"Orphan tickets (opened one hour ago, still not assigned)" => "SELECT UserRequest AS u WHERE u.start_date < DATE_SUB(NOW(), INTERVAL 60 MINUTE) AND u.status = 'new'",
|
||||
"Long lasting incidents (duration > 8 hours)" => "SELECT UserRequest AS u WHERE u.close_date > DATE_ADD(u.start_date, INTERVAL 8 HOUR)",
|
||||
),
|
||||
);
|
||||
|
||||
$aDisplayData = array();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForForm();
|
||||
foreach ($aExamples as $sTopic => $aQueries)
|
||||
{
|
||||
foreach ($aQueries as $sDescription => $sOql)
|
||||
{
|
||||
$sHighlight = '';
|
||||
$sDisable = '';
|
||||
if ($sOql == $sExpression)
|
||||
{
|
||||
// this one is currently being tested, highlight it
|
||||
$sHighlight = "background-color:yellow;";
|
||||
$sDisable = 'disabled';
|
||||
// and remember we are testing a query of the list
|
||||
$bUsingExample = true;
|
||||
}
|
||||
//$aDisplayData[$sTopic][] = array(
|
||||
$aDisplayData[Dict::S('UI:RunQuery:QueryExamples')][] = array(
|
||||
'desc' => "<div style=\"$sHighlight\">".utils::EscapeHtml($sDescription)."</div>",
|
||||
'oql' => "<div style=\"$sHighlight\">".utils::EscapeHtml($sOql)."</div>",
|
||||
'go' => "<form method=\"get\"><input type=\"hidden\" name=\"expression\" value=\"$sOql\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Test')."\" $sDisable>$sContext</form>\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['desc'] = array('label' => Dict::S('UI:RunQuery:HeaderPurpose'), 'description' => Dict::S('UI:RunQuery:HeaderPurpose+'));
|
||||
$aDisplayConfig['oql'] = array('label' => Dict::S('UI:RunQuery:HeaderOQLExpression'), 'description' => Dict::S('UI:RunQuery:HeaderOQLExpression+'));
|
||||
$aDisplayConfig['go'] = array('label' => '', 'description' => '');
|
||||
|
||||
foreach ($aDisplayData as $sTopic => $aQueriesDisplayData)
|
||||
{
|
||||
$bShowOpened = $bUsingExample;
|
||||
$oP->StartCollapsibleSection($sTopic, $bShowOpened);
|
||||
$oP->table($aDisplayConfig, $aQueriesDisplayData);
|
||||
$oP->EndCollapsibleSection();
|
||||
}
|
||||
}
|
||||
|
||||
$sOperation = utils::ReadParam('operation', 'menu');
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
$oP = new iTopWebPage(Dict::S('UI:RunQuery:Title'));
|
||||
$oP->SetBreadCrumbEntry('ui-tool-runquery', Dict::S('Menu:RunQueriesMenu'), Dict::S('Menu:RunQueriesMenu+'), '', 'fas fa-terminal', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||
|
||||
// Main program
|
||||
$sExpression = utils::ReadParam('expression', '', false, 'raw_data');
|
||||
$sEncoding = utils::ReadParam('encoding', 'oql');
|
||||
|
||||
ShowExamples($oP, $sExpression);
|
||||
|
||||
try
|
||||
{
|
||||
if ($sEncoding == 'crypted')
|
||||
{
|
||||
// Translate $sExpression into a oql expression
|
||||
$sClearText = base64_decode($sExpression);
|
||||
echo "<strong>FYI: '$sClearText'</strong><br/>\n";
|
||||
$oFilter = DBObjectSearch::unserialize($sExpression);
|
||||
$sExpression = $oFilter->ToOQL();
|
||||
}
|
||||
|
||||
$oFilter = null;
|
||||
$aArgs = array();
|
||||
$sSyntaxError = null;
|
||||
|
||||
if (!empty($sExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sExpression);
|
||||
} catch (Exception $e)
|
||||
{
|
||||
if ($e instanceof OqlException)
|
||||
{
|
||||
$sSyntaxError = $e->getHtmlDesc();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSyntaxError = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if ($oFilter)
|
||||
{
|
||||
$aArgs = array();
|
||||
foreach ($oFilter->GetQueryParams() as $sParam => $foo)
|
||||
{
|
||||
$value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data');
|
||||
if (!is_null($value))
|
||||
{
|
||||
$aArgs[$sParam] = $value;
|
||||
}
|
||||
else {
|
||||
$aArgs[$sParam] = '';
|
||||
}
|
||||
}
|
||||
$oFilter->SetInternalParams($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."<br/>\n");
|
||||
$oP->add("<textarea cols=\"120\" rows=\"8\" id=\"expression\" name=\"expression\">".utils::EscapeHtml($sExpression)."</textarea>\n");
|
||||
$oP->add_linked_script(utils::GetAbsoluteUrlAppRoot()."/js/jquery.hotkeys.js");
|
||||
$oP->add_ready_script(<<<EOF
|
||||
$("#expression").select();
|
||||
$("#expression").on("keydown", null, "ctrl+return", function() {
|
||||
$(this).closest("form").submit();
|
||||
});
|
||||
EOF
|
||||
);
|
||||
|
||||
if (count($aArgs) > 0)
|
||||
{
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oP->add("<h3>Query arguments</h3>\n");
|
||||
foreach ($aArgs as $sParam => $sValue)
|
||||
{
|
||||
$oP->p("$sParam: <input type=\"string\" name=\"arg_$sParam\" value=\"$sValue\">\n");
|
||||
}
|
||||
$oP->add("</div>\n");
|
||||
}
|
||||
|
||||
$oP->add("<input type=\"submit\" value=\"".Dict::S('UI:Button:Evaluate')."\" title=\"".Dict::S('UI:Button:Evaluate:Title')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
|
||||
|
||||
if ($oFilter)
|
||||
{
|
||||
$oP->add("<h3>Query results</h3>\n");
|
||||
|
||||
$oResultBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oResultBlock->Display($oP, 'runquery');
|
||||
|
||||
// Breadcrumb
|
||||
//$iCount = $oResultBlock->GetDisplayedCount();
|
||||
$sPageId = "ui-search-".$oFilter->GetClass();
|
||||
$sLabel = MetaModel::GetName($oFilter->GetClass());
|
||||
$aArgs = array();
|
||||
foreach (array_merge($_POST, $_GET) as $sKey => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$aItems = array();
|
||||
foreach ($value as $sItemKey => $sItemValue)
|
||||
{
|
||||
$aArgs[] = $sKey.'['.$sItemKey.']='.urlencode($sItemValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aArgs[] = $sKey.'='.urlencode($value);
|
||||
}
|
||||
}
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/run_query.php?'.implode('&', $aArgs);
|
||||
$oP->SetBreadCrumbEntry($sPageId, $sLabel, $oFilter->ToOQL(true), $sUrl, 'fas fa-terminal', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||
|
||||
$oP->p('');
|
||||
$oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false, 'runQuery');
|
||||
$oP->p('<pre>'.$oFilter->ToOQL().'</pre>');
|
||||
$oP->EndCollapsibleSection();
|
||||
|
||||
$aModifierProperties = MetaModel::MakeModifierProperties($oFilter);
|
||||
|
||||
$oP->StartCollapsibleSection('OQL Developed');
|
||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter);
|
||||
$oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
|
||||
$oP->p('<pre>'.$oSQLObjectQueryBuilder->DebugOQLClassTree($oBuild).'</pre>');
|
||||
$oP->EndCollapsibleSection();
|
||||
|
||||
$oP->StartCollapsibleSection('SQL Count');
|
||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter);
|
||||
$oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
|
||||
$oSQLQueryCount = $oSQLObjectQueryBuilder->BuildSQLQueryStruct(null, true, $aModifierProperties);
|
||||
$oP->p('<pre>'.$oSQLQueryCount->RenderSelect(array(), array(), 0, 0, true, true).'</pre>');
|
||||
$oP->EndCollapsibleSection();
|
||||
|
||||
$oP->StartCollapsibleSection('SQL');
|
||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oFilter);
|
||||
$oBuild = new QueryBuilderContext($oFilter, $aModifierProperties);
|
||||
$oSQLQuery = $oSQLObjectQueryBuilder->BuildSQLQueryStruct(null, false, $aModifierProperties);
|
||||
$oP->p('<pre>'.$oSQLQuery->RenderSelect(array(), array(), 10, 0, false, true).'</pre>');
|
||||
$oP->EndCollapsibleSection();
|
||||
|
||||
}
|
||||
elseif ($sSyntaxError)
|
||||
{
|
||||
if ($e instanceof OqlException)
|
||||
{
|
||||
$sWrongWord = $e->GetWrongWord();
|
||||
$aSuggestedWords = $e->GetSuggestions();
|
||||
if (count($aSuggestedWords) > 0)
|
||||
{
|
||||
$sSuggestedWord = OqlException::FindClosestString($sWrongWord, $aSuggestedWords);
|
||||
|
||||
if (strlen($sSuggestedWord) > 0) {
|
||||
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->GetIssue().' <em>'.$sWrongWord).'</em></b>');
|
||||
$sBefore = substr($sExpression, 0, $e->GetColumn());
|
||||
$sAfter = substr($sExpression, $e->GetColumn() + strlen($sWrongWord));
|
||||
$sFixedExpression = $sBefore.$sSuggestedWord.$sAfter;
|
||||
$sFixedExpressionHtml = $sBefore.'<span style="background-color:yellow">'.$sSuggestedWord.'</span>'.$sAfter;
|
||||
$oP->p("Suggesting: $sFixedExpressionHtml");
|
||||
$oP->add('<button onClick="$(\'textarea[name=expression]\').val(\''.utils::EscapeHtml(addslashes($sFixedExpression)).'\');">Use this query</button>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</b>');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</b>');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getMessage()).'</b>');
|
||||
}
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$oP->p('<b>'.Dict::Format('UI:RunQuery:Error', $e->getMessage()).'</b>');
|
||||
}
|
||||
|
||||
$oP->output();
|
||||
|
||||
862
tests/php-unit-tests/legacy-tests/benchmark.php
Normal file
@@ -0,0 +1,862 @@
|
||||
<?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('../../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/wizardhelper.class.inc.php');
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
|
||||
//ini_set('memory_limit', '2048M');
|
||||
|
||||
class BenchmarkDataCreation
|
||||
{
|
||||
var $m_iIfByServer;
|
||||
var $m_iIfByNWDevice;
|
||||
var $m_aRequested;
|
||||
var $m_aPlanned;
|
||||
var $m_aCreatedByClass = array();
|
||||
var $m_aCreatedByDesc = array();
|
||||
|
||||
var $m_aStatsByClass = array();
|
||||
|
||||
/** @var \CMDBChange $m_oChange */
|
||||
var $m_oChange;
|
||||
public function __construct()
|
||||
{
|
||||
CMDBObject::SetTrackInfo('Benchmark setup');
|
||||
}
|
||||
|
||||
public function PlanStructure($iPlannedContacts, $iPlannedContracts)
|
||||
{
|
||||
$this->m_aRequested = array(
|
||||
'plannedcontacts' => $iPlannedContacts,
|
||||
'plannedcontracts' => $iPlannedContracts,
|
||||
);
|
||||
$this->m_aPlanned = array(
|
||||
'Contacts' => $iPlannedContacts,
|
||||
'Contracts' => $iPlannedContracts,
|
||||
'Documents' => $iPlannedContracts * 2,
|
||||
);
|
||||
}
|
||||
|
||||
public function PlanCis($iPlannedCIs)
|
||||
{
|
||||
$this->m_aRequested = array(
|
||||
'plannedcis' => $iPlannedCIs,
|
||||
);
|
||||
|
||||
$this->m_iIfByServer = 2;
|
||||
$this->m_iIfByNWDevice = 10;
|
||||
|
||||
$iServers = ceil($iPlannedCIs * 9 / 10);
|
||||
$iNWDevices = ceil($iPlannedCIs / 10);
|
||||
$iInterfaces = $iServers * $this->m_iIfByServer + $iNWDevices * $this->m_iIfByNWDevice;
|
||||
$iApplications = $iServers * 5;
|
||||
$iSolutions = ceil($iApplications / 2);
|
||||
$iProcesses = ceil($iSolutions / 2);
|
||||
|
||||
$this->m_aPlanned = array(
|
||||
'Network devices' => $iNWDevices,
|
||||
'Servers' => $iServers,
|
||||
'Interfaces' => $iInterfaces,
|
||||
'Application SW' => 2,
|
||||
'Applications' => $iApplications,
|
||||
'Solutions' => $iSolutions,
|
||||
'Processes' => $iProcesses,
|
||||
);
|
||||
}
|
||||
|
||||
public function PlanTickets($iPlannedTickets, $iBigTicketCis)
|
||||
{
|
||||
$this->m_aRequested = array(
|
||||
'plannedtickets' => $iPlannedTickets,
|
||||
'plannedbigticketcis' => $iBigTicketCis,
|
||||
);
|
||||
|
||||
$this->m_aPlanned = array(
|
||||
'Incidents' => ceil($iPlannedTickets / 2),
|
||||
'Changes' => ceil($iPlannedTickets / 2),
|
||||
'Big ticket: CIs' => $iBigTicketCis,
|
||||
);
|
||||
}
|
||||
|
||||
public function ShowPlans($oP)
|
||||
{
|
||||
$oP->add("<h2>Planned creations</h2>\n");
|
||||
$aPlanned = $this->m_aPlanned;
|
||||
$aForm = array();
|
||||
foreach ($aPlanned as $sKey => $iCount)
|
||||
{
|
||||
$aForm[] = array(
|
||||
'label' => $sKey,
|
||||
'input' => $iCount,
|
||||
);
|
||||
}
|
||||
$oP->form($aForm);
|
||||
}
|
||||
|
||||
public function ShowForm($oP, $sNextOperation)
|
||||
{
|
||||
$aRequested = $this->m_aRequested;
|
||||
$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Loading data...', 10)\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sNextOperation\">\n");
|
||||
foreach($this->m_aRequested as $sName => $sValue)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\">\n");
|
||||
}
|
||||
$oP->add("<button type=\"submit\">Next >></button>\n");
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
|
||||
protected function CreateObject($sClass, $aData, $sClassDesc = '')
|
||||
{
|
||||
$mu_t1 = MyHelpers::getmicrotime();
|
||||
|
||||
$oMyObject = MetaModel::NewObject($sClass);
|
||||
foreach($aData as $sProp => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
// transform into a link set
|
||||
$sCSVSpec = implode('|', $value);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sProp);
|
||||
$value = $oAttDef->MakeValueFromString($sCSVSpec, $bLocalizedValue = false, $sSepItem = '|', $sSepAttribute = ';', $sSepValue = ':', $sAttributeQualifier = '"');
|
||||
}
|
||||
$oMyObject->Set($sProp, $value);
|
||||
}
|
||||
|
||||
$iId = $oMyObject->DBInsertNoReload();
|
||||
|
||||
$sClassId = "$sClass ($sClassDesc)";
|
||||
$this->m_aCreatedByDesc[$sClassId][] = $iId;
|
||||
$this->m_aCreatedByClass[$sClass][] = $iId;
|
||||
|
||||
$mu_t2 = MyHelpers::getmicrotime();
|
||||
$this->m_aStatsByClass[$sClass][] = $mu_t2 - $mu_t1;
|
||||
|
||||
return $iId;
|
||||
}
|
||||
|
||||
static $m_aClassIdCache = array();
|
||||
protected function GetClassIds($sClass)
|
||||
{
|
||||
if (!isset(self::$m_aClassIdCache[$sClass]))
|
||||
{
|
||||
// Load the cache now
|
||||
self::$m_aClassIdCache[$sClass] = array();
|
||||
|
||||
$oSet = new DBObjectSet(new DBObjectSearch($sClass));
|
||||
while($oObj = $oSet->Fetch())
|
||||
{
|
||||
self::$m_aClassIdCache[$sClass][] = $oObj->GetKey();
|
||||
}
|
||||
}
|
||||
return self::$m_aClassIdCache[$sClass];
|
||||
}
|
||||
|
||||
protected function RandomId($sClass, $sClassDesc = '')
|
||||
{
|
||||
$sClassId = "$sClass ($sClassDesc)";
|
||||
if (isset($this->m_aCreatedByDesc[$sClassId]))
|
||||
{
|
||||
return $this->m_aCreatedByDesc[$sClassId][array_rand($this->m_aCreatedByDesc[$sClassId])];
|
||||
}
|
||||
|
||||
$aIds = self::GetClassIds($sClass);
|
||||
return $aIds[array_rand($aIds)];
|
||||
}
|
||||
|
||||
static protected function FindId($sClass)
|
||||
{
|
||||
$oSet = new DBObjectSet(new DBObjectSearch($sClass));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$oObj = $oSet->Fetch();
|
||||
return $oObj->GetKey();
|
||||
}
|
||||
|
||||
static protected function FindIdFromOQL($sOQL)
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$oObj = $oSet->Fetch();
|
||||
return $oObj->GetKey();
|
||||
}
|
||||
|
||||
protected function my_array_rand($aData, $iCount)
|
||||
{
|
||||
if ($iCount == 0)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
elseif ($iCount == 1)
|
||||
{
|
||||
// array_rand() for one item returns only the key
|
||||
$key = array_rand($aData);
|
||||
$aSample = array($key);
|
||||
}
|
||||
elseif ($iCount <= count($aData))
|
||||
{
|
||||
$aSample = array_rand($aData, $iCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSample = array_merge(array_keys($aData), self::my_array_rand($aData, $iCount - count($aData)));
|
||||
}
|
||||
return $aSample;
|
||||
}
|
||||
|
||||
protected function CreateLinks($iFrom, $iCount, $sLinkClass, $sAttCodeFrom, $sAttCodeTo)
|
||||
{
|
||||
$oAttTo = MetaModel::GetAttributeDef($sLinkClass, $sAttCodeTo);
|
||||
$sToClass = $oAttTo->GetTargetClass();
|
||||
|
||||
$aTargets = self::GetClassIds($sToClass);
|
||||
$aSample = self::my_array_rand($aTargets, $iCount);
|
||||
|
||||
foreach($aSample as $key)
|
||||
{
|
||||
$aData = array(
|
||||
$sAttCodeFrom => $iFrom,
|
||||
$sAttCodeTo => $aTargets[$key],
|
||||
);
|
||||
$this->CreateObject($sLinkClass, $aData);
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateStructure($oP)
|
||||
{
|
||||
$aClasses = MetaModel::GetClasses();
|
||||
$aActions = array('Read', 'Bulk Read', 'Delete', 'Bulk Delete', 'Modify', 'Bulk Modify');
|
||||
$aStdProfiles = array(2, 3, 4, 5, 6, 7, 8, 9);
|
||||
|
||||
////////////////////////////////////////
|
||||
// New specific profile, giving access to everything
|
||||
//
|
||||
$aData = array(
|
||||
'name' => 'Data guru',
|
||||
'description' => 'Could do anything, because everything is granted',
|
||||
);
|
||||
$iGuruProfile = $this->CreateObject('URP_Profiles', $aData);
|
||||
foreach($aClasses as $sClass)
|
||||
{
|
||||
foreach($aActions as $sAction)
|
||||
{
|
||||
$aData = array(
|
||||
'profileid' => $iGuruProfile,
|
||||
'class' => $sClass,
|
||||
'permission' => 'yes',
|
||||
'action' => $sAction,
|
||||
);
|
||||
$this->CreateObject('URP_ActionGrant', $aData);
|
||||
}
|
||||
}
|
||||
|
||||
// User login with super access rights
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => self::FindId('Organization'),
|
||||
'location_id' => self::FindId('Location'),
|
||||
'first_name' => 'Jesus',
|
||||
'name' => 'Deus',
|
||||
'email' => 'guru@combodo.com',
|
||||
);
|
||||
$iPerson = $this->CreateObject('Person', $aData);
|
||||
$aData = array(
|
||||
'contactid' => $iPerson,
|
||||
'login' => 'guru',
|
||||
'password' => 'guru',
|
||||
'language' => 'EN US',
|
||||
'profile_list' => array("profileid:$iGuruProfile;reason:he is the one"),
|
||||
);
|
||||
$iLogin = $this->CreateObject('UserLocal', $aData);
|
||||
|
||||
////////////////////////////////////////
|
||||
// User login having all std profiles
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => self::FindId('Organization'),
|
||||
'location_id' => self::FindId('Location'),
|
||||
'first_name' => 'Little ze',
|
||||
'name' => 'Foo',
|
||||
'email' => 'foo@combodo.com',
|
||||
);
|
||||
$iPerson = $this->CreateObject('Person', $aData);
|
||||
|
||||
$aProfileSet = array();
|
||||
foreach($aStdProfiles as $iProfileId)
|
||||
{
|
||||
$aProfileSet[] = "profileid:$iProfileId;reason:xxx";
|
||||
}
|
||||
$aData = array(
|
||||
'contactid' => $iPerson,
|
||||
'login' => 'foo',
|
||||
'password' => 'foo',
|
||||
'language' => 'EN US',
|
||||
'profile_list' => $aProfileSet,
|
||||
);
|
||||
$iLogin = $this->CreateObject('UserLocal', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Organizations
|
||||
//
|
||||
$aData = array(
|
||||
'name' => 'Benchmark',
|
||||
);
|
||||
$iOrg = $this->CreateObject('Organization', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Locations
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'name' => 'Rio de Janeiro',
|
||||
);
|
||||
$iLoc = $this->CreateObject('Location', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Teams
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'location_id' => $iLoc,
|
||||
'name' => 'Fluminense',
|
||||
'email' => 'fluminense@combodo.com',
|
||||
);
|
||||
$iTeam = $this->CreateObject('Team', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Persons
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Contacts'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'location_id' => $iLoc,
|
||||
'first_name' => 'Joaõ',
|
||||
'name' => 'Ningem #'.$i,
|
||||
'email' => 'foo'.$i.'@nowhere.fr',
|
||||
);
|
||||
$iPerson = $this->CreateObject('Person', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
//
|
||||
$aData = array(
|
||||
'contact_id' => $iPerson,
|
||||
'team_id' => $this->RandomId('Team'),
|
||||
);
|
||||
$this->CreateObject('lnkTeamToContact', $aData);
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Services
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'name' => 'My Service',
|
||||
);
|
||||
$iService = $this->CreateObject('Service', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Service subcategories
|
||||
//
|
||||
$aData = array(
|
||||
'name' => 'My subcategory',
|
||||
'service_id' => $iService,
|
||||
);
|
||||
$iOrg = $this->CreateObject('ServiceSubcategory', $aData);
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Contracts
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Contracts'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'name' => "Contract #$i",
|
||||
'description' => 'Created for benchmarking purposes',
|
||||
'org_id' => $this->RandomId('Organization'),
|
||||
'provider_id' => $this->RandomId('Organization'),
|
||||
'start_date' => '2009-12-25',
|
||||
'end_date' => '2019-08-01',
|
||||
'support_team_id' => $this->RandomId('Team'),
|
||||
);
|
||||
$iContract = $this->CreateObject('CustomerContract', $aData);
|
||||
|
||||
// Contract/Contact (10% of contacts)
|
||||
//
|
||||
$iContactCount = ceil($this->m_aPlanned['Contracts'] / 10);
|
||||
for($iLinked = 0 ; $iLinked < $iContactCount ; $iLinked++)
|
||||
{
|
||||
$aData = array(
|
||||
'contact_id' => $this->RandomId('Person'),
|
||||
'contract_id' => $iContract,
|
||||
'role' => 'role '.$iLinked,
|
||||
);
|
||||
$this->CreateObject('lnkContractToContact', $aData);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Documents
|
||||
//
|
||||
$sMyDoc = '';
|
||||
for($i = 0 ; $i < 1000 ; $i++)
|
||||
{
|
||||
// 100 chars
|
||||
$sMyDoc .= "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678\n";
|
||||
}
|
||||
$oRefDoc = new ormDocument($sMyDoc, 'text/plain');
|
||||
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Documents'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'name' => "document$i",
|
||||
'contents' => $oRefDoc,
|
||||
);
|
||||
$this->CreateObject('FileDoc', $aData);
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateCis($oP)
|
||||
{
|
||||
$iOrg = $this->FindIdFromOQL("SELECT Organization WHERE name = 'Benchmark'");
|
||||
$iLoc = $this->FindIdFromOQL("SELECT Location WHERE org_id = $iOrg");
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Servers
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Servers'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'location_id' => $iLoc,
|
||||
'name' => 'server'.$i,
|
||||
'status' => 'production',
|
||||
);
|
||||
$iServer = $this->CreateObject('Server', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
$this->CreateLinks($iServer, 1, 'lnkContractToCI', 'ci_id', 'contract_id');
|
||||
|
||||
// Interfaces
|
||||
for($iLinked = 0 ; $iLinked < $this->m_iIfByServer ; $iLinked++)
|
||||
{
|
||||
$aData = array(
|
||||
'name' => "eth$iLinked",
|
||||
'status' => 'implementation',
|
||||
'org_id' => $iOrg,
|
||||
'device_id' => $iServer,
|
||||
'status' => 'production',
|
||||
);
|
||||
$this->CreateObject('NetworkInterface', $aData, 'server if');
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Network devices
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Network devices'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'location_id' => $iLoc,
|
||||
'name' => 'equipment #'.$i,
|
||||
'status' => 'production',
|
||||
);
|
||||
$iNWDevice = $this->CreateObject('NetworkDevice', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
$this->CreateLinks($iNWDevice, 1, 'lnkContractToCI', 'ci_id', 'contract_id');
|
||||
|
||||
// Interfaces
|
||||
//
|
||||
for($iLinked = 0 ; $iLinked < $this->m_iIfByNWDevice ; $iLinked++)
|
||||
{
|
||||
$aData = array(
|
||||
'name' => "eth$iLinked",
|
||||
'status' => 'implementation',
|
||||
'org_id' => $iOrg,
|
||||
'device_id' => $iNWDevice,
|
||||
'connected_if' => $this->RandomId('NetworkInterface', 'server if'),
|
||||
'status' => 'production',
|
||||
);
|
||||
$this->CreateObject('NetworkInterface', $aData, 'equipment if');
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Application Software
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Application SW'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'name' => 'Software #'.$i,
|
||||
);
|
||||
$iNWDevice = $this->CreateObject('Application', $aData);
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Applications
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Applications'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'device_id' => $this->RandomId('Server'),
|
||||
'software_id' => $this->RandomId('Application'),
|
||||
'name' => 'Application #'.$i,
|
||||
'status' => 'production',
|
||||
);
|
||||
$iAppInstance = $this->CreateObject('ApplicationInstance', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
$this->CreateLinks($iAppInstance, 1, 'lnkContractToCI', 'ci_id', 'contract_id');
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Application Solution
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Solutions'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'name' => 'Solution #'.$i,
|
||||
'status' => 'production',
|
||||
);
|
||||
$iAppSolution = $this->CreateObject('ApplicationSolution', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
$this->CreateLinks($iAppSolution, 1, 'lnkContractToCI', 'ci_id', 'contract_id');
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Business Process
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Processes'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'name' => 'Process #'.$i,
|
||||
'status' => 'production',
|
||||
);
|
||||
$iProcess = $this->CreateObject('BusinessProcess', $aData);
|
||||
|
||||
// Contract/Infra
|
||||
$this->CreateLinks($iProcess, 1, 'lnkContractToCI', 'ci_id', 'contract_id');
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateTickets($oP)
|
||||
{
|
||||
$iOrg = $this->FindIdFromOQL("SELECT Organization WHERE name = 'Benchmark'");
|
||||
$iLoc = $this->FindIdFromOQL("SELECT Location WHERE org_id = $iOrg");
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Incident Tickets
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Incidents'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'caller_id' => $this->RandomId('Person'),
|
||||
'workgroup_id' => $this->RandomId('Team'),
|
||||
'agent_id' => $this->RandomId('Person'),
|
||||
'service_id' => $this->RandomId('Service'),
|
||||
'servicesubcategory_id' => $this->RandomId('ServiceSubcategory'),
|
||||
'title' => 'Incident #'.$i,
|
||||
'description' => 'O que aconteceu?',
|
||||
'ticket_log' => 'Testing...',
|
||||
);
|
||||
$iTicket = $this->CreateObject('Incident', $aData);
|
||||
|
||||
// Incident/Infra
|
||||
$iInfraCount = rand(1, 6);
|
||||
$this->CreateLinks($iTicket, $iInfraCount, 'lnkTicketToCI', 'ticket_id', 'ci_id');
|
||||
|
||||
// Incident/Infra
|
||||
$iContactCount = rand(1, 6);
|
||||
$this->CreateLinks($iTicket, $iContactCount, 'lnkTicketToContact', 'ticket_id', 'contact_id');
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Big Ticket
|
||||
//
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'caller_id' => $this->RandomId('Person'),
|
||||
'workgroup_id' => $this->RandomId('Team'),
|
||||
'agent_id' => $this->RandomId('Person'),
|
||||
'service_id' => $this->RandomId('Service'),
|
||||
'servicesubcategory_id' => $this->RandomId('ServiceSubcategory'),
|
||||
'title' => 'Big ticket',
|
||||
'description' => 'O que aconteceu?',
|
||||
'ticket_log' => 'Testing...',
|
||||
);
|
||||
$iTicket = $this->CreateObject('Incident', $aData);
|
||||
|
||||
// Incident/Infra
|
||||
$iInfraCount = $this->m_aPlanned['Big ticket: CIs'];
|
||||
$this->CreateLinks($iTicket, $iInfraCount, 'lnkTicketToCI', 'ticket_id', 'ci_id');
|
||||
|
||||
// Incident/Infra
|
||||
$iContactCount = rand(1, 6);
|
||||
$this->CreateLinks($iTicket, $iContactCount, 'lnkTicketToContact', 'ticket_id', 'contact_id');
|
||||
|
||||
/////////////////////////
|
||||
//
|
||||
// Change Tickets
|
||||
//
|
||||
for($i = 0 ; $i < $this->m_aPlanned['Changes'] ; $i++)
|
||||
{
|
||||
$aData = array(
|
||||
'org_id' => $iOrg,
|
||||
'requestor_id' => $this->RandomId('Person'),
|
||||
'workgroup_id' => $this->RandomId('Team'),
|
||||
'agent_id' => $this->RandomId('Person'),
|
||||
'supervisor_group_id' => $this->RandomId('Team'),
|
||||
'supervisor_id' => $this->RandomId('Person'),
|
||||
'manager_group_id' => $this->RandomId('Team'),
|
||||
'manager_id' => $this->RandomId('Person'),
|
||||
'title' => 'change #'.$i,
|
||||
'description' => "Let's do something there",
|
||||
);
|
||||
$iTicket = $this->CreateObject('NormalChange', $aData);
|
||||
|
||||
// Incident/Infra
|
||||
$iInfraCount = rand(1, 6);
|
||||
$this->CreateLinks($iTicket, $iInfraCount, 'lnkTicketToCI', 'ticket_id', 'ci_id');
|
||||
|
||||
// Incident/Infra
|
||||
$iContactCount = rand(1, 6);
|
||||
$this->CreateLinks($iTicket, $iContactCount, 'lnkTicketToContact', 'ticket_id', 'contact_id');
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeFeedback($oP)
|
||||
{
|
||||
foreach($this->m_aCreatedByClass as $sClass => $aClassIds)
|
||||
{
|
||||
$iSample = reset($aClassIds);
|
||||
$sSample = "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=details&class=$sClass&id=$iSample\">sample</a>";
|
||||
|
||||
$iDuration = number_format(array_sum($this->m_aStatsByClass[$sClass]), 3);
|
||||
$fDurationMin = number_format(min($this->m_aStatsByClass[$sClass]), 3);
|
||||
$fDurationMax = number_format(max($this->m_aStatsByClass[$sClass]), 3);
|
||||
$fDurationAverage = number_format(array_sum($this->m_aStatsByClass[$sClass]) / count($this->m_aStatsByClass[$sClass]), 3);
|
||||
|
||||
$oP->add("<ul>");
|
||||
$oP->add("<li>");
|
||||
$oP->add("$sClass: ".count($this->m_aStatsByClass[$sClass])." - $sSample<br/>");
|
||||
$oP->add("Duration: $fDurationMin => $fDurationMax; Avg:$fDurationAverage; Total: $iDuration");
|
||||
$oP->add("</li>");
|
||||
$oP->add("</ul>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the user what are the settings for the data load
|
||||
*/
|
||||
function DisplayStep1(SetupPage $oP)
|
||||
{
|
||||
$sNextOperation = 'step2';
|
||||
$oP->add("<h1>iTop benchmarking</h1>\n");
|
||||
|
||||
$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Evaluating real plans...', 10)\">\n");
|
||||
$oP->add("<fieldset><legend>Data load configuration</legend>\n");
|
||||
$aForm = array();
|
||||
$aForm[] = array(
|
||||
'label' => "Contacts:",
|
||||
'input' => "<input id=\"from\" type=\"text\" name=\"plannedcontacts\" value=\"100\">",
|
||||
'help' => '',
|
||||
);
|
||||
$aForm[] = array(
|
||||
'label' => "Contracts:",
|
||||
'input' => "<input id=\"from\" type=\"text\" name=\"plannedcontracts\" value=\"10\">",
|
||||
'help' => '',
|
||||
);
|
||||
$oP->form($aForm);
|
||||
$oP->add("</fieldset>\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_structure\">\n");
|
||||
$oP->add("<button type=\"submit\">Next >></button>\n");
|
||||
$oP->add("</form>\n");
|
||||
|
||||
$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Evaluating real plans...', 10)\">\n");
|
||||
$oP->add("<fieldset><legend>Data load configuration</legend>\n");
|
||||
$aForm = array();
|
||||
$aForm[] = array(
|
||||
'label' => "Main CIs:",
|
||||
'input' => "<input id=\"to\" type=\"text\" name=\"plannedcis\" value=\"70\">",
|
||||
'help' => ' exclude interfaces, subnets or any other type of secondary CI',
|
||||
);
|
||||
$oP->form($aForm);
|
||||
$oP->add("</fieldset>\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_cis\">\n");
|
||||
$oP->add("<button type=\"submit\">Next >></button>\n");
|
||||
$oP->add("</form>\n");
|
||||
|
||||
$oP->add("<form method=\"post\" onSubmit=\"return DoSubmit('Evaluating real plans...', 10)\">\n");
|
||||
$oP->add("<fieldset><legend>Data load configuration</legend>\n");
|
||||
$aForm = array();
|
||||
$aForm[] = array(
|
||||
'label' => "Tickets:",
|
||||
'input' => "<input id=\"to\" type=\"text\" name=\"plannedtickets\" value=\"200\">",
|
||||
'help' => ' 50% incidents, 50% changes',
|
||||
);
|
||||
$aForm[] = array(
|
||||
'label' => "CIs for the big ticket:",
|
||||
'input' => "<input id=\"to\" type=\"text\" name=\"plannedbigticketcis\" value=\"200\">",
|
||||
'help' => 'Number of CI for the single big ticket',
|
||||
);
|
||||
$oP->form($aForm);
|
||||
$oP->add("</fieldset>\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"create_tickets\">\n");
|
||||
$oP->add("<button type=\"submit\">Next >></button>\n");
|
||||
$oP->add("</form>\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main program
|
||||
*/
|
||||
|
||||
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
|
||||
|
||||
$sOperation = Utils::ReadParam('operation', 'step1');
|
||||
$oP = new SetupPage('iTop benchmark utility');
|
||||
|
||||
ExecutionKPI::EnableDuration();
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
try
|
||||
{
|
||||
switch($sOperation)
|
||||
{
|
||||
case 'step1':
|
||||
DisplayStep1($oP);
|
||||
break;
|
||||
|
||||
case 'create_structure':
|
||||
$oP->no_cache();
|
||||
$oP->add_xframe_options('DENY');
|
||||
$iPlannedContacts = Utils::ReadParam('plannedcontacts');
|
||||
$iPlannedContracts = Utils::ReadParam('plannedcontracts');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanStructure($iPlannedContacts, $iPlannedContracts);
|
||||
$oDataCreation->ShowPlans($oP);
|
||||
$oDataCreation->ShowForm($oP, 'create_structure_go');
|
||||
break;
|
||||
|
||||
case 'create_structure_go':
|
||||
$oP->no_cache();
|
||||
$iPlannedContacts = Utils::ReadParam('plannedcontacts');
|
||||
$iPlannedContracts = Utils::ReadParam('plannedcontracts');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanStructure($iPlannedContacts, $iPlannedContracts);
|
||||
$oDataCreation->CreateStructure($oP);
|
||||
$oDataCreation->MakeFeedback($oP);
|
||||
break;
|
||||
|
||||
case 'create_cis':
|
||||
$oP->no_cache();
|
||||
$iPlannedCIs = Utils::ReadParam('plannedcis');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanCis($iPlannedCIs);
|
||||
$oDataCreation->ShowPlans($oP);
|
||||
$oDataCreation->ShowForm($oP, 'create_cis_go');
|
||||
break;
|
||||
|
||||
case 'create_cis_go':
|
||||
$oP->no_cache();
|
||||
$iPlannedCIs = Utils::ReadParam('plannedcis');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanCis($iPlannedCIs);
|
||||
$oDataCreation->CreateCis($oP);
|
||||
$oDataCreation->MakeFeedback($oP);
|
||||
break;
|
||||
|
||||
case 'create_tickets':
|
||||
$oP->no_cache();
|
||||
$iPlannedTickets = Utils::ReadParam('plannedtickets');
|
||||
$iBigTicketCis = Utils::ReadParam('plannedbigticketcis');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanTickets($iPlannedTickets, $iBigTicketCis);
|
||||
$oDataCreation->ShowPlans($oP);
|
||||
$oDataCreation->ShowForm($oP, 'create_tickets_go');
|
||||
break;
|
||||
|
||||
case 'create_tickets_go':
|
||||
$oP->no_cache();
|
||||
$iPlannedTickets = Utils::ReadParam('plannedtickets');
|
||||
$iBigTicketCis = Utils::ReadParam('plannedbigticketcis');
|
||||
|
||||
$oDataCreation = new BenchmarkDataCreation();
|
||||
$oDataCreation->PlanTickets($iPlannedTickets, $iBigTicketCis);
|
||||
$oDataCreation->CreateTickets($oP);
|
||||
$oDataCreation->MakeFeedback($oP);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oP->error("Error: unsupported operation '$sOperation'");
|
||||
}
|
||||
}
|
||||
catch(ZZException $e)
|
||||
{
|
||||
$oP->error("Error: '".$e->getMessage()."'");
|
||||
}
|
||||
catch(ZZCoreException $e)
|
||||
{
|
||||
$oP->error("Error: '".$e->getHtmlDesc()."'");
|
||||
}
|
||||
$oKPI->ComputeAndReport('Total execution');
|
||||
//DBSearch::RecordQueryTrace();
|
||||
$oP->output();
|
||||
?>
|
||||
146
tests/php-unit-tests/legacy-tests/build_test_oql.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-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
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
/**
|
||||
* Date: 06/10/2017
|
||||
*/
|
||||
|
||||
|
||||
require_once('../../../approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
\LoginWebPage::DoLogin(true);
|
||||
|
||||
$sOQLFile = APPROOT.'log/oql_records.txt';
|
||||
$sTestFile = APPROOT.'tests/core/oql_records.php';
|
||||
|
||||
$oTestHandle = @fopen($sTestFile, "w");
|
||||
|
||||
@fwrite($oTestHandle, "<?php\n\n");
|
||||
|
||||
$aFoundOQLs = array();
|
||||
$iCount = 0;
|
||||
$iRead = 0;
|
||||
|
||||
$oOQLHandle = @fopen($sOQLFile, "r");
|
||||
if ($oOQLHandle) {
|
||||
while (($sBuffer = fgets($oOQLHandle)) !== false) {
|
||||
$iRead++;
|
||||
$aRecord = unserialize($sBuffer);
|
||||
|
||||
$sOQL = $aRecord['oql'];
|
||||
|
||||
$sChecksum = md5($sBuffer);
|
||||
if (isset($aFoundOQLs[$sChecksum])) { continue; }
|
||||
$aFoundOQLs[$sChecksum] = true;
|
||||
|
||||
$iCount++;
|
||||
$sOrderBy = ConvertArray($aRecord['order_by']);
|
||||
$sAttToLoad = ConvertArray($aRecord['att_to_load']);
|
||||
$iLimitCount = $aRecord['limit_count'];
|
||||
$iLimitStart = $aRecord['limit_start'];
|
||||
|
||||
// $sOQL, $aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart
|
||||
|
||||
$sLine = "\$aData[\"SELECT $iCount\"] = array(\"$sOQL\", $sOrderBy, array(), $sAttToLoad, array(), $iLimitCount, $iLimitStart);\n";
|
||||
@fwrite($oTestHandle, $sLine);
|
||||
}
|
||||
if (!feof($oOQLHandle)) {
|
||||
echo "Erreur: fgets() a échoué\n";
|
||||
}
|
||||
@fclose($oOQLHandle);
|
||||
}
|
||||
@fwrite($oTestHandle, "\n");
|
||||
|
||||
@fclose($oTestHandle);
|
||||
|
||||
echo "File '$sTestFile' generated with $iCount entries (from $iRead captured OQL).\n";
|
||||
|
||||
|
||||
/// Group by
|
||||
|
||||
|
||||
$sOQLFile = APPROOT.'log/oql_group_by_records.txt';
|
||||
$sTestFile = APPROOT.'tests/core/oql_group_by_records.php';
|
||||
|
||||
$oTestHandle = @fopen($sTestFile, "w");
|
||||
|
||||
@fwrite($oTestHandle, "<?php\n\n");
|
||||
|
||||
$aFoundOQLs = array();
|
||||
$iCount = 1000;
|
||||
$iRead = 0;
|
||||
|
||||
$oOQLHandle = @fopen($sOQLFile, "r");
|
||||
if ($oOQLHandle) {
|
||||
while (($sBuffer = fgets($oOQLHandle)) !== false) {
|
||||
$iRead++;
|
||||
$aRecord = unserialize($sBuffer);
|
||||
|
||||
$sOQL = $aRecord['oql'];
|
||||
|
||||
$sChecksum = md5($sBuffer);
|
||||
if (isset($aFoundOQLs[$sChecksum])) { continue; }
|
||||
$aFoundOQLs[$sChecksum] = true;
|
||||
|
||||
$iCount++;
|
||||
$sOrderBy = ConvertArray($aRecord['order_by']);
|
||||
$sGroupByExpr = ConvertArray($aRecord['group_by_expr']);
|
||||
$sSelectExpr = ConvertArray($aRecord['select_expr']);
|
||||
if ($aRecord['exclude_null_values'])
|
||||
{
|
||||
$bExcludeNullValues = 'true';
|
||||
}
|
||||
else
|
||||
{
|
||||
$bExcludeNullValues = 'false';
|
||||
}
|
||||
$iLimitCount = $aRecord['limit_count'];
|
||||
$iLimitStart = $aRecord['limit_start'];
|
||||
|
||||
// $sOQL, $aArgs, $aGroupByExpr, $bExcludeNullValues, $aSelectExpr, $aOrderBy, $iLimitCount, $iLimitStart
|
||||
|
||||
$sLine = "\$aData[\"SELECT $iCount\"] = array(\"$sOQL\", array(), $sGroupByExpr, $bExcludeNullValues, $sSelectExpr, $sOrderBy, $iLimitCount, $iLimitStart);\n";
|
||||
@fwrite($oTestHandle, $sLine);
|
||||
}
|
||||
if (!feof($oOQLHandle)) {
|
||||
echo "Erreur: fgets() a échoué\n";
|
||||
}
|
||||
@fclose($oOQLHandle);
|
||||
}
|
||||
@fwrite($oTestHandle, "\n");
|
||||
|
||||
@fclose($oTestHandle);
|
||||
|
||||
echo "<br>File '$sTestFile' generated with ".($iCount-1000)." entries (from $iRead captured OQL).\n";
|
||||
|
||||
function ConvertArray($aArray)
|
||||
{
|
||||
if (is_null($aArray))
|
||||
{
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (empty($aArray))
|
||||
{
|
||||
return 'array()';
|
||||
}
|
||||
|
||||
return 'unserialize(\''.str_replace("'", "\\'",serialize($aArray)).'\')';
|
||||
}
|
||||
38
tests/php-unit-tests/legacy-tests/config-test-farm.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
//
|
||||
// phpMyORM configuration file
|
||||
//
|
||||
// To be manually edited (or generated by the configuration wizard)
|
||||
//
|
||||
// The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
|
||||
//
|
||||
|
||||
$MySettings = array(
|
||||
'db_host' => 'localhost',
|
||||
'db_user' => 'root',
|
||||
'db_pwd' => '',
|
||||
'db_name' => 'TestFarm',
|
||||
'db_subname' => '', // use it to differentiate two applications instances running on the same DB
|
||||
);
|
||||
|
||||
// Modules: file names should be specified as a absolute paths
|
||||
|
||||
$MyModules = array(
|
||||
'application' => array (
|
||||
// '../core/event.class.inc.php',
|
||||
// '../core/action.class.inc.php',
|
||||
// '../core/trigger.class.inc.php',
|
||||
// to be continued...
|
||||
),
|
||||
'business' => array (
|
||||
'../business/test_farm.class.inc.php',
|
||||
// to be continued...
|
||||
),
|
||||
'addons' => array (
|
||||
//'user rights' => '/addons/userrights/userrightsnull.class.inc.php', // or userrightsmatrix.class.inc.php
|
||||
// other modules to come later
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
||||
104
tests/php-unit-tests/legacy-tests/display_cache_content.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Date: 06/10/2017
|
||||
*/
|
||||
|
||||
require_once('../../../approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
|
||||
$sEnvironment = MetaModel::GetEnvironmentId();
|
||||
$aEntries = array();
|
||||
$aCacheUserData = apc_cache_info_compat();
|
||||
if (is_array($aCacheUserData) && isset($aCacheUserData['cache_list']))
|
||||
{
|
||||
$sPrefix = 'itop-'.$sEnvironment.'-query-cache-';
|
||||
|
||||
foreach($aCacheUserData['cache_list'] as $i => $aEntry)
|
||||
{
|
||||
$sEntryKey = array_key_exists('info', $aEntry) ? $aEntry['info'] : $aEntry['key'];
|
||||
if (strpos($sEntryKey, $sPrefix) === 0)
|
||||
{
|
||||
$aEntries[] = $sEntryKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "<pre>";
|
||||
|
||||
if (empty($aEntries))
|
||||
{
|
||||
echo "No Data";
|
||||
return;
|
||||
}
|
||||
|
||||
$sKey = $aEntries[0];
|
||||
$result = apc_fetch($sKey);
|
||||
if (!is_object($result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$oSQLQuery = $result;
|
||||
|
||||
echo "NB Tables before;NB Tables after;";
|
||||
foreach($oSQLQuery->m_aContextData as $sField => $oValue)
|
||||
{
|
||||
echo $sField.';';
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
sort($aEntries);
|
||||
|
||||
foreach($aEntries as $sKey)
|
||||
{
|
||||
$result = apc_fetch($sKey);
|
||||
if (is_object($result))
|
||||
{
|
||||
$oSQLQuery = $result;
|
||||
if (isset($oSQLQuery->m_aContextData))
|
||||
{
|
||||
echo $oSQLQuery->m_iOriginalTableCount.";".$oSQLQuery->CountTables().';';
|
||||
foreach($oSQLQuery->m_aContextData as $oValue)
|
||||
{
|
||||
if (is_array($oValue))
|
||||
{
|
||||
$sVal = json_encode($oValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (empty($oValue))
|
||||
{
|
||||
$sVal = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sVal = $oValue;
|
||||
}
|
||||
}
|
||||
echo $sVal.';';
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
|
||||
353
tests/php-unit-tests/legacy-tests/replay_query_log.php
Normal file
@@ -0,0 +1,353 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
function LogResult($sString)
|
||||
{
|
||||
file_put_contents(APPROOT.'data/queries.results.log', "\n".$sString, FILE_APPEND);
|
||||
}
|
||||
|
||||
function LogBenchmarkCSV()
|
||||
{
|
||||
$aValues = array();
|
||||
foreach (func_get_args() as $arg)
|
||||
{
|
||||
if (is_string($arg))
|
||||
{
|
||||
$aValues[] = '"'.str_replace('"', '""', $arg).'"';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues[] = (string) $arg;
|
||||
}
|
||||
}
|
||||
$sLine = implode(';', $aValues); // the preferred for MS Excel
|
||||
file_put_contents(APPROOT.'data/queries.benchmark.csv', "\n".$sLine, FILE_APPEND);
|
||||
}
|
||||
|
||||
class QueryLogEntry
|
||||
{
|
||||
public function __construct($aLogEntryId, $aLogEntryData)
|
||||
{
|
||||
$this->aErrors = array();
|
||||
$this->sSql = '';
|
||||
$this->MakeDuration = 0;
|
||||
$this->fExecDuration = 0;
|
||||
$this->iTableCount = 0;
|
||||
$this->aRows = array();
|
||||
|
||||
$this->sLogId = $aLogEntryId;
|
||||
$this->sOql = $aLogEntryData['oql'];
|
||||
$this->sOqlHtml = htmlentities($this->sOql, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
|
||||
$aQueryData = unserialize($aLogEntryData['data']);
|
||||
$this->oFilter = $aQueryData['filter'];
|
||||
$this->sClass = $this->oFilter->GetClass();
|
||||
$this->aArgs = $aQueryData['args'];
|
||||
|
||||
$iRepeat = utils::ReadParam('repeat', 3);
|
||||
|
||||
if ($aQueryData['type'] == 'select')
|
||||
{
|
||||
$this->aOrderBy = $aQueryData['order_by'];
|
||||
$this->aAttToLoad = $aQueryData['att_to_load'];
|
||||
$this->aExtendedDataSpec = $aQueryData['extended_data_spec'];
|
||||
$this->iLimitCount = $aQueryData['limit_count'];
|
||||
$this->iLimitStart = $aQueryData['limit_start'];
|
||||
$this->bGetCount = $aQueryData['is_count'];
|
||||
|
||||
if ($this->bGetCount)
|
||||
{
|
||||
$this->sQueryType = 'COUNT';
|
||||
$this->sQueryDesc = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sQueryType = 'LIST';
|
||||
$this->sQueryDesc = "limit count: $this->iLimitCount";
|
||||
$this->sQueryDesc .= "; limit start: $this->iLimitStart";
|
||||
if (count($this->aOrderBy) > 0)
|
||||
{
|
||||
$this->sQueryDesc .= "; order by: ".implode(',', array_keys($this->aOrderBy));
|
||||
}
|
||||
if (is_array($this->aAttToLoad))
|
||||
{
|
||||
$this->sQueryDesc .= "; attributes: ".implode(',', array_keys($this->aAttToLoad));
|
||||
}
|
||||
}
|
||||
|
||||
$fRefTime = MyHelpers::getmicrotime();
|
||||
try
|
||||
{
|
||||
for($i = 0 ; $i < $iRepeat ; $i++)
|
||||
{
|
||||
$this->sSql = $this->oFilter->MakeSelectQuery($this->aOrderBy, $this->aArgs, $this->aAttToLoad, $this->aExtendedDataSpec, $this->iLimitCount, $this->iLimitStart, $this->bGetCount);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->aErrors[] = "Failed to create the SQL:".$e->getMessage();
|
||||
}
|
||||
$this->fMakeDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
|
||||
}
|
||||
elseif ($aQueryData['type'] == 'group_by')
|
||||
{
|
||||
$this->aGroupByExpr = $aQueryData['group_by_expr'];
|
||||
|
||||
$this->sQueryType = 'GROUP BY';
|
||||
$aGroupedBy = array();
|
||||
foreach ($this->aGroupByExpr as $oExpr)
|
||||
{
|
||||
$aGroupedBy[] = $oExpr->RenderExpression();
|
||||
}
|
||||
$this->sQueryDesc = implode(', ', $aGroupedBy);
|
||||
|
||||
$fRefTime = MyHelpers::getmicrotime();
|
||||
try
|
||||
{
|
||||
for($i = 0 ; $i < $iRepeat ; $i++)
|
||||
{
|
||||
$this->sSql = $this->oFilter->MakeGroupByQuery($this->aArgs, $this->aGroupByExpr);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$this->aErrors[] = "Failed to create the SQL:".$e->getMessage();
|
||||
}
|
||||
$this->fMakeDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unsupported
|
||||
$this->sQueryType = 'ERROR';
|
||||
$this->sQueryDesc = "Unkown type of query: ".$aQueryData['type'];
|
||||
}
|
||||
}
|
||||
|
||||
public function Exec()
|
||||
{
|
||||
if ($this->sSql != '')
|
||||
{
|
||||
$iRepeat = utils::ReadParam('repeat', 3);
|
||||
try
|
||||
{
|
||||
$resQuery = null;
|
||||
$fRefTime = MyHelpers::getmicrotime();
|
||||
for($i = 0 ; $i < $iRepeat ; $i++)
|
||||
{
|
||||
$resQuery = CMDBSource::Query($this->sSql);
|
||||
}
|
||||
$this->fExecDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
|
||||
|
||||
// This is not relevant...
|
||||
if (preg_match_all('|\s*JOIN\s*\(\s*`|', $this->sSql, $aMatches)) // JOIN (`mytable...
|
||||
{
|
||||
$this->iTableCount = 1 + count($aMatches[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->iTableCount = 1;
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->aErrors[] = "Failed to execute the SQL:".$e->getMessage();
|
||||
}
|
||||
if ($resQuery)
|
||||
{
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$this->aRows[] = $aRow;
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function HasErrors()
|
||||
{
|
||||
return (count($this->aErrors) > 0);
|
||||
}
|
||||
|
||||
public function Display($oP)
|
||||
{
|
||||
$oP->p($this->sOqlHtml);
|
||||
$oP->p($this->sQueryType);
|
||||
$oP->p($this->sQueryDesc);
|
||||
foreach ($this->aErrors as $sError)
|
||||
{
|
||||
$oP->p($sError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main program
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
require_once('../../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
$operation = utils::ReadParam('operation', '');
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
|
||||
|
||||
$oP = new WebPage('Replay queries.log');
|
||||
|
||||
|
||||
ini_set('memory_limit', '512M');
|
||||
|
||||
require_once(APPROOT.'/data/queries.log');
|
||||
$iCount = count($aQueriesLog);
|
||||
$oP->p("Nombre de requêtes: ".$iCount);
|
||||
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
|
||||
switch ($sOperation)
|
||||
{
|
||||
case '':
|
||||
default:
|
||||
$oP->add("<ol>\n");
|
||||
foreach ($aQueriesLog as $sQueryId => $aOqlData)
|
||||
{
|
||||
$sOql = $aOqlData['oql'];
|
||||
$sOqlHtml = htmlentities($sOql, ENT_QUOTES, 'UTF-8');
|
||||
$oP->add("<li>$sOqlHtml <a href=\"?operation=zoom&query=$sQueryId\">zoom</a></li>\n");
|
||||
}
|
||||
$oP->add("</ol>\n");
|
||||
|
||||
$oP->add("<form action=\"?operation=benchmark&repeat=3\" method=\"post\">\n");
|
||||
$oP->add("<input type=\"submit\" value=\"Benchmark (3 repeats)!\">\n");
|
||||
$oP->add("</form>\n");
|
||||
|
||||
$oP->add("<form action=\"?operation=check\" method=\"post\">\n");
|
||||
$oP->add("<input type=\"submit\" value=\"Check!\">\n");
|
||||
$oP->add("</form>\n");
|
||||
break;
|
||||
|
||||
case 'zoom':
|
||||
$sQueryId = utils::ReadParam('query', '', false, 'raw_data');
|
||||
$oP->add("<h2>Zoom on query</h2>\n");
|
||||
$oQuery = new QueryLogEntry($sQueryId, $aQueriesLog[$sQueryId]);
|
||||
$oQuery->Exec();
|
||||
$oQuery->Display($oP);
|
||||
|
||||
$oP->add("<pre>$oQuery->sSql</pre>\n");
|
||||
$oP->p("Tables: $oQuery->iTableCount");
|
||||
|
||||
if (strlen($oQuery->sSql) > 0)
|
||||
{
|
||||
$aExplain = CMDBSource::ExplainQuery($oQuery->sSql);
|
||||
$oP->add("<h4>Explain</h4>\n");
|
||||
$oP->add("<table style=\"border=1px;\">\n");
|
||||
foreach ($aExplain as $aRow)
|
||||
{
|
||||
$oP->add(" <tr>\n");
|
||||
$oP->add(" <td>".implode('</td><td>', $aRow)."</td>\n");
|
||||
$oP->add(" </tr>\n");
|
||||
}
|
||||
$oP->add("</table>\n");
|
||||
}
|
||||
|
||||
if (count($oQuery->aRows))
|
||||
{
|
||||
$oP->add("<h4>Values</h4>\n");
|
||||
$oP->add("<table style=\"border=1px;\">\n");
|
||||
foreach ($oQuery->aRows as $iRow => $aRow)
|
||||
{
|
||||
$oP->add(" <tr>\n");
|
||||
$oP->add(" <td>".implode('</td><td>', $aRow)."</td>\n");
|
||||
$oP->add(" </tr>\n");
|
||||
}
|
||||
$oP->add("</table>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p("No data");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 'check':
|
||||
$oP->add("<h2>List queries in error</h2>\n");
|
||||
foreach ($aQueriesLog as $sQueryId => $aOqlData)
|
||||
{
|
||||
$oQuery = new QueryLogEntry($sQueryId, $aOqlData);
|
||||
$oQuery->Exec();
|
||||
|
||||
if ($oQuery->HasErrors())
|
||||
{
|
||||
$oQuery->Display($oP);
|
||||
$oP->p("<a href=\"?operation=zoom&query=$sQueryId\">zoom</a>");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'benchmark':
|
||||
$oP->add("<h2>Create data/queries.xxx reports</h2>\n");
|
||||
// Reset the log contents
|
||||
file_put_contents(APPROOT.'data/queries.results.log', date('Y-m-d H:i:s')."\n");
|
||||
file_put_contents(APPROOT.'data/queries.benchmark.csv', '');
|
||||
LogBenchmarkCSV('type', 'properties', 'make duration', 'class', 'tables', 'query length', 'exec duration', 'rows', 'oql');
|
||||
|
||||
$iErrors = 0;
|
||||
|
||||
foreach ($aQueriesLog as $sQueryId => $aOqlData)
|
||||
{
|
||||
$oQuery = new QueryLogEntry($sQueryId, $aOqlData);
|
||||
$oQuery->Exec();
|
||||
|
||||
LogResult('-----------------------------------------------------------');
|
||||
LogResult($oQuery->sOql);
|
||||
LogResult($oQuery->sQueryType);
|
||||
if (strlen($oQuery->sQueryDesc) > 0)
|
||||
{
|
||||
LogResult($oQuery->sQueryDesc);
|
||||
}
|
||||
|
||||
if ($oQuery->HasErrors())
|
||||
{
|
||||
foreach($oQuery->aErrors as $sError)
|
||||
{
|
||||
LogResult($sError);
|
||||
$iErrors++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogResult("row count = ".count($oQuery->aRows));
|
||||
foreach($oQuery->aRows as $iRow => $aRow)
|
||||
{
|
||||
LogResult("row: ".serialize($aRow));
|
||||
if ($iRow > 100) break;
|
||||
}
|
||||
|
||||
LogBenchmarkCSV($oQuery->sQueryType, $oQuery->sQueryDesc, sprintf('%1.3f', round($oQuery->fMakeDuration, 3)), $oQuery->sClass, $oQuery->iTableCount, strlen($oQuery->sSql), sprintf('%1.4f', round($oQuery->fExecDuration, 4)), count($oQuery->aRows), $oQuery->sOql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oP->output();
|
||||
?>
|
||||
551
tests/php-unit-tests/legacy-tests/test.class.inc.php
Normal file
@@ -0,0 +1,551 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-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
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Core automated tests - basics
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
require_once(APPROOT.'/core/attributedef.class.inc.php');
|
||||
require_once(APPROOT.'/core/filterdef.class.inc.php');
|
||||
require_once(APPROOT.'/core/stimulus.class.inc.php');
|
||||
require_once(APPROOT.'/core/MyHelpers.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/core/oql/expression.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/core/sqlquery.class.inc.php');
|
||||
require_once(APPROOT.'/core/sqlobjectquery.class.inc.php');
|
||||
require_once(APPROOT.'/core/sqlunionquery.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/core/log.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
require_once(APPROOT.'/core/dbsearch.class.php');
|
||||
require_once(APPROOT.'/core/dbobjectset.class.php');
|
||||
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/core/userrights.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/webservices/webservices.class.inc.php');
|
||||
|
||||
|
||||
// Just to differentiate programmatically triggered exceptions and other kind of errors (usefull?)
|
||||
class UnitTestException extends Exception
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Improved display of the backtrace
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ExceptionFromError extends Exception
|
||||
{
|
||||
public function getTraceAsHtml()
|
||||
{
|
||||
$aBackTrace = $this->getTrace();
|
||||
return MyHelpers::get_callstack_html(0, $this->getTrace());
|
||||
// return "<pre>\n".$this->getTraceAsString()."</pre>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test handler API and basic helpers
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestHandler
|
||||
{
|
||||
protected $m_aSuccesses;
|
||||
protected $m_aWarnings;
|
||||
protected $m_aErrors;
|
||||
protected $m_sOutput;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aSuccesses = array();
|
||||
$this->m_aWarnings = array();
|
||||
$this->m_aErrors = array();
|
||||
}
|
||||
|
||||
static public function GetName() {return "fooname";}
|
||||
static public function GetDescription(){return "foodesc";}
|
||||
|
||||
protected function DoPrepare() {return true;}
|
||||
abstract protected function DoExecute();
|
||||
protected function DoCleanup() {return true;}
|
||||
|
||||
protected static function DumpVariable($var)
|
||||
{
|
||||
echo "<pre class=\"vardump\">\n";
|
||||
print_r($var);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
protected function ReportSuccess($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aSuccesses[] = $sMessage;
|
||||
}
|
||||
|
||||
protected function ReportWarning($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aWarnings[] = $sMessage;
|
||||
}
|
||||
|
||||
protected function ReportError($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aErrors[] = $sMessage;
|
||||
}
|
||||
|
||||
public function GetResults()
|
||||
{
|
||||
return $this->m_aSuccesses;
|
||||
}
|
||||
|
||||
public function GetWarnings()
|
||||
{
|
||||
return $this->m_aWarnings;
|
||||
}
|
||||
|
||||
public function GetErrors()
|
||||
{
|
||||
return $this->m_aErrors;
|
||||
}
|
||||
|
||||
public function GetOutput()
|
||||
{
|
||||
return $this->m_sOutput;
|
||||
}
|
||||
|
||||
public function error_handler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
// Note: return false to call the default handler (stop the program if an error)
|
||||
|
||||
if ($errstr == 'assert()') $errno = E_USER_ERROR;
|
||||
|
||||
switch ($errno)
|
||||
{
|
||||
case E_USER_ERROR:
|
||||
case E_WARNING: //(assertion failed)
|
||||
$this->ReportError("$errfile@$errline - $errstr");
|
||||
break;
|
||||
case E_USER_WARNING:
|
||||
$this->ReportWarning("$errfile@$errline - $errstr");
|
||||
break;
|
||||
case E_USER_NOTICE:
|
||||
$this->ReportWarning("$errfile@$errline - $errstr");
|
||||
break;
|
||||
default:
|
||||
$this->ReportWarning("$errfile@$errline - Unknown error type: [$errno] $errstr");
|
||||
echo "Unknown error type: [$errno] $errstr in $errfile at $errline<br />\n";
|
||||
break;
|
||||
}
|
||||
return true; // do not call the default handler
|
||||
}
|
||||
|
||||
public function Execute()
|
||||
{
|
||||
ob_start();
|
||||
set_error_handler(array($this, 'error_handler'));
|
||||
try
|
||||
{
|
||||
$this->DoPrepare();
|
||||
$this->DoExecute();
|
||||
}
|
||||
catch (ExceptionFromError $e)
|
||||
{
|
||||
$this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
|
||||
}
|
||||
catch (CoreException $e)
|
||||
{
|
||||
//$this->ReportError($e->getMessage());
|
||||
//$this->ReportError($e->__tostring());
|
||||
$this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
//$this->ReportError($e->getMessage());
|
||||
//$this->ReportError($e->__tostring());
|
||||
$this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString());
|
||||
}
|
||||
restore_error_handler();
|
||||
$this->m_sOutput = ob_get_clean();
|
||||
return (count($this->GetErrors()) == 0);
|
||||
}
|
||||
|
||||
static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = 'admin', $sOptionnalHeaders = null)
|
||||
{
|
||||
$aDataAndAuth = $aData;
|
||||
// To be changed to use basic authentication
|
||||
$aDataAndAuth['operation'] = 'login';
|
||||
$aDataAndAuth['auth_user'] = $sLogin;
|
||||
$aDataAndAuth['auth_pwd'] = $sPassword;
|
||||
$sHost = $_SERVER['HTTP_HOST'];
|
||||
$sRawPath = $_SERVER['SCRIPT_NAME'];
|
||||
$sPath = dirname($sRawPath);
|
||||
$sUrl = "http://$sHost/$sPath/$sRelativeUrl";
|
||||
|
||||
return self::DoPostRequest($sUrl, $aDataAndAuth, $sOptionnalHeaders);
|
||||
}
|
||||
|
||||
// Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||
// originaly named after do_post_request
|
||||
// Partially adapted to our coding conventions
|
||||
static protected function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null)
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
$sData = http_build_query($aData);
|
||||
|
||||
$aParams = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'content' => $sData,
|
||||
'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
|
||||
));
|
||||
if ($sOptionnalHeaders !== null)
|
||||
{
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
global $php_errormsg;
|
||||
if (isset($php_errormsg))
|
||||
{
|
||||
throw new Exception("Problem with $sUrl, $php_errormsg");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Problem with $sUrl");
|
||||
}
|
||||
}
|
||||
$response = @stream_get_contents($fp);
|
||||
if ($response === false)
|
||||
{
|
||||
throw new Exception("Problem reading data from $sUrl, $php_errormsg");
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a piece of code (checks if an error occurs)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestFunction extends TestHandler
|
||||
{
|
||||
// simply overload DoExecute (temporary)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a piece of code (checks if an error occurs)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestWebServices extends TestHandler
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to execute a piece of code (checks if an error occurs)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestSoapWebService extends TestHandler
|
||||
{
|
||||
// simply overload DoExecute (temporary)
|
||||
|
||||
function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check that a function outputs some values depending on its input
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestFunctionInOut extends TestFunction
|
||||
{
|
||||
// abstract static public function GetCallSpec(); // parameters to call_user_func
|
||||
// abstract static public function GetInOut(); // array of input => output
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$aTests = $this->GetInOut();
|
||||
if (is_array($aTests))
|
||||
{
|
||||
foreach ($aTests as $iTestId => $aTest)
|
||||
{
|
||||
$ret = call_user_func_array($this->GetCallSpec(), $aTest['args']);
|
||||
if ($ret != $aTest['output'])
|
||||
{
|
||||
// Note: to be improved to cope with non string parameters
|
||||
$this->ReportError("Found '$ret' while expecting '".$aTest['output']."'", $iTestId);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->ReportSuccess("Found the expected output '$ret'", $iTestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = call_user_func($this->GetCallSpec());
|
||||
$this->ReportSuccess('Finished successfully');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to check an URL (Searches for Error/Warning/Etc keywords)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestUrl extends TestHandler
|
||||
{
|
||||
// abstract static public function GetUrl();
|
||||
// abstract static public function GetErrorKeywords();
|
||||
// abstract static public function GetWarningKeywords();
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to check a user management module
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestUserRights extends TestHandler
|
||||
{
|
||||
protected function DoExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a scenario on a given DB
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestScenarioOnDB extends TestHandler
|
||||
{
|
||||
// abstract static public function GetDBHost();
|
||||
// abstract static public function GetDBUser();
|
||||
// abstract static public function GetDBPwd();
|
||||
// abstract static public function GetDBName();
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
$sDBHost = $this->GetDBHost();
|
||||
$sDBUser = $this->GetDBUser();
|
||||
$sDBPwd = $this->GetDBPwd();
|
||||
$sDBName = $this->GetDBName();
|
||||
|
||||
CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd);
|
||||
CMDBSource::SetCharacterSet();
|
||||
if (CMDBSource::IsDB($sDBName))
|
||||
{
|
||||
CMDBSource::DropDB($sDBName);
|
||||
}
|
||||
CMDBSource::CreateDB($sDBName);
|
||||
}
|
||||
|
||||
protected function DoCleanup()
|
||||
{
|
||||
// CMDBSource::DropDB($this->GetDBName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to use a business model on a given DB
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestBizModel extends TestHandler
|
||||
{
|
||||
// abstract static public function GetDBSubName();
|
||||
// abstract static public function GetBusinessModelFile();
|
||||
// abstract static public function GetConfigFile();
|
||||
|
||||
static public function GetConfigFile() {return 'conf/production/config-itop.php';}
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
$sConfigFile = APPROOT.$this->GetConfigFile();
|
||||
MetaModel::Startup($sConfigFile);
|
||||
// #@# Temporary disabled by Romain
|
||||
// MetaModel::CheckDefinitions();
|
||||
|
||||
// something here to create records... but that's another story
|
||||
}
|
||||
|
||||
protected $m_oChange;
|
||||
protected function GetCurrentChange()
|
||||
{
|
||||
if (!isset($this->m_oChange))
|
||||
{
|
||||
new CMDBChange();
|
||||
$oMyChange = MetaModel::NewObject("CMDBChange");
|
||||
$oMyChange->Set("date", time());
|
||||
$oMyChange->Set("userinfo", "Someone doing some tests");
|
||||
$iChangeId = $oMyChange->DBInsertNoReload();
|
||||
$this->m_oChange = $oMyChange;
|
||||
}
|
||||
return $this->m_oChange;
|
||||
}
|
||||
protected function ObjectToDB($oNew, $bReload = false)
|
||||
{
|
||||
if ($bReload)
|
||||
{
|
||||
$iId = $oNew->DBInsert();
|
||||
}
|
||||
else
|
||||
{
|
||||
$iId = $oNew->DBInsertNoReload();
|
||||
}
|
||||
return $iId;
|
||||
}
|
||||
|
||||
protected function UpdateObjectInDB($oObject)
|
||||
{
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
protected function ResetDB()
|
||||
{
|
||||
if (MetaModel::DBExists(false))
|
||||
{
|
||||
MetaModel::DBDrop();
|
||||
}
|
||||
MetaModel::DBCreate();
|
||||
}
|
||||
|
||||
static protected function show_list($oObjectSet)
|
||||
{
|
||||
$oObjectSet->Rewind();
|
||||
$aData = array();
|
||||
while ($oItem = $oObjectSet->Fetch())
|
||||
{
|
||||
$aValues = array();
|
||||
foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode)
|
||||
{
|
||||
$aValues[$sAttCode] = $oItem->GetAsHTML($sAttCode);
|
||||
}
|
||||
//echo $oItem->GetKey()." => ".implode(", ", $aValues)."</br>\n";
|
||||
$aData[] = $aValues;
|
||||
}
|
||||
echo MyHelpers::make_table_from_assoc_array($aData);
|
||||
}
|
||||
|
||||
static protected function search_and_show_list(DBSearch $oMyFilter)
|
||||
{
|
||||
$oObjSet = new CMDBObjectSet($oMyFilter);
|
||||
echo $oMyFilter->ToOQL()."' - Found ".$oObjSet->Count()." items.</br>\n";
|
||||
self::show_list($oObjSet);
|
||||
}
|
||||
|
||||
static protected function search_and_show_list_from_oql($sOQL)
|
||||
{
|
||||
echo $sOQL."...<br/>\n";
|
||||
$oNewFilter = DBObjectSearch::FromOQL($sOQL);
|
||||
self::search_and_show_list($oNewFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a scenario common to any business model (tries to build all the possible queries, etc.)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class TestBizModelGeneric extends TestBizModel
|
||||
{
|
||||
static public function GetName()
|
||||
{
|
||||
return 'Full test on a given business model';
|
||||
}
|
||||
|
||||
static public function GetDescription()
|
||||
{
|
||||
return 'Systematic tests: gets each and every existing class and tries every attribute, search filters, etc.';
|
||||
}
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
parent::DoPrepare();
|
||||
|
||||
if (!MetaModel::DBExists(false))
|
||||
{
|
||||
MetaModel::DBCreate();
|
||||
}
|
||||
// something here to create records... but that's another story
|
||||
}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
foreach(MetaModel::GetClasses() as $sClassName)
|
||||
{
|
||||
if (MetaModel::HasTable($sClassName)) continue;
|
||||
|
||||
$oNobody = MetaModel::GetObject($sClassName, 123);
|
||||
$oBaby = new $sClassName;
|
||||
$oFilter = new DBObjectSearch($sClassName);
|
||||
|
||||
// Challenge reversibility of OQL / filter object
|
||||
//
|
||||
$sExpr1 = $oFilter->ToOQL();
|
||||
$oNewFilter = DBObjectSearch::FromOQL($sExpr1);
|
||||
$sExpr2 = $oNewFilter->ToOQL();
|
||||
if ($sExpr1 != $sExpr2)
|
||||
{
|
||||
$this->ReportError("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>");
|
||||
}
|
||||
|
||||
// Use the filter (perform the query)
|
||||
//
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
162
tests/php-unit-tests/legacy-tests/test.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
?>
|
||||
<style>
|
||||
.vardump {
|
||||
font-size:8pt;
|
||||
line-height:100%;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function ReadMandatoryParam($sName)
|
||||
{
|
||||
$value = utils::ReadParam($sName, null);
|
||||
if (is_null($value))
|
||||
{
|
||||
echo "<p>Missing mandatory argument <b>$sName</b></p>";
|
||||
exit;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
function IsAValidTestClass($sClassName)
|
||||
{
|
||||
// Must be a child of TestHandler
|
||||
//
|
||||
if (!is_subclass_of($sClassName, 'TestHandler')) return false;
|
||||
|
||||
// Must not be abstract
|
||||
//
|
||||
$oReflectionClass = new ReflectionClass($sClassName);
|
||||
if (!$oReflectionClass->isInstantiable()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function GetTestClassLine($sClassName)
|
||||
{
|
||||
$oReflectionClass = new ReflectionClass($sClassName);
|
||||
return $oReflectionClass->getStartLine();
|
||||
}
|
||||
|
||||
function DisplayEvents($aEvents, $sTitle)
|
||||
{
|
||||
echo "<h4>$sTitle</h4>\n";
|
||||
if (count($aEvents) > 0)
|
||||
{
|
||||
echo "<ul>\n";
|
||||
foreach ($aEvents as $sEvent)
|
||||
{
|
||||
echo "<li>$sEvent</li>\n";
|
||||
}
|
||||
echo "</ul>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<p>none</p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
date_default_timezone_set('Europe/Paris');
|
||||
|
||||
require_once('../../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once('./test.class.inc.php');
|
||||
require_once('./testlist.inc.php');
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
|
||||
|
||||
$sTodo = utils::ReadParam("todo", "");
|
||||
if ($sTodo == '')
|
||||
{
|
||||
// Show the list of tests
|
||||
//
|
||||
echo "<h3>Existing tests</h3>\n";
|
||||
echo "<ul>\n";
|
||||
foreach (get_declared_classes() as $sClassName)
|
||||
{
|
||||
if (!IsAValidTestClass($sClassName)) continue;
|
||||
|
||||
$sName = call_user_func(array($sClassName, 'GetName'));
|
||||
$sDescription = call_user_func(array($sClassName, 'GetDescription'));
|
||||
echo "<li><a href=\"?todo=exec&testid=$sClassName\">$sName</a> ($sDescription)</li>\n";
|
||||
}
|
||||
echo "</ul>\n";
|
||||
}
|
||||
else if ($sTodo == 'exec')
|
||||
{
|
||||
// Execute a test
|
||||
//
|
||||
$sTestClass = ReadMandatoryParam("testid");
|
||||
|
||||
if (!IsAValidTestClass($sTestClass))
|
||||
{
|
||||
echo "<p>Wrong value for testid, expecting a valid class name</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$oTest = new $sTestClass();
|
||||
$iStartLine = GetTestClassLine($sTestClass);
|
||||
echo "<h3>Testing: ".$oTest->GetName()."</h3>\n";
|
||||
echo "<h6>testlist.inc.php: $iStartLine</h6>\n";
|
||||
$bRes = $oTest->Execute();
|
||||
}
|
||||
|
||||
/*
|
||||
MyHelpers::var_dump_html($oTest->GetResults());
|
||||
MyHelpers::var_dump_html($oTest->GetWarnings());
|
||||
MyHelpers::var_dump_html($oTest->GetErrors());
|
||||
*/
|
||||
|
||||
if ($bRes)
|
||||
{
|
||||
echo "<p>Success :-)</p>\n";
|
||||
DisplayEvents($oTest->GetResults(), 'Results');
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<p>Failure :-(</p>\n";
|
||||
}
|
||||
DisplayEvents($oTest->GetErrors(), 'Errors');
|
||||
DisplayEvents($oTest->GetWarnings(), 'Warnings');
|
||||
|
||||
// Render the output
|
||||
//
|
||||
echo "<h4>Actual output</h4>\n";
|
||||
echo "<div style=\"border: dashed; background-color:light-grey;\">\n";
|
||||
echo $oTest->GetOutput();
|
||||
echo "</div>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
4114
tests/php-unit-tests/legacy-tests/testlist.inc.php
Normal file
84
tests/php-unit-tests/phpunit.xml.dist
Normal file
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd"
|
||||
bootstrap="unittestautoload.php"
|
||||
backupGlobals="true"
|
||||
colors="true"
|
||||
columns="120"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnRisky="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="true"
|
||||
printerClass="\Sempro\PHPUnitPrettyPrinter\PrettyPrinterForPhpUnit9"
|
||||
>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL"/>
|
||||
<ini name="display_errors" value="On"/>
|
||||
<ini name="log_errors" value="On"/>
|
||||
<ini name="html_errors" value="Off"/>
|
||||
<env name="PHPUNIT_PRETTY_PRINT_PROGRESS" value="true"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<!-- Unitary tests -->
|
||||
<testsuite name="Application">
|
||||
<directory>unitary-tests/application</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Core">
|
||||
<directory>unitary-tests/core</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Datamodels">
|
||||
<directory>unitary-tests/datamodels/2.x</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Setup">
|
||||
<directory>unitary-tests/setup</directory>
|
||||
</testsuite>
|
||||
<!-- Note: The unitary-tests/sources/application/TwigBase is omitted for now as the test is not working -->
|
||||
<testsuite name="SourcesApplicationSearch">
|
||||
<directory>unitary-tests/sources/application/search</directory>
|
||||
</testsuite>
|
||||
<testsuite name="SourceApplicationStatus">
|
||||
<directory>unitary-tests/sources/application/status</directory>
|
||||
</testsuite>
|
||||
<testsuite name="SourcesComposer">
|
||||
<directory>unitary-tests/sources/Composer</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Synchro">
|
||||
<directory>unitary-tests/synchro</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Webservices">
|
||||
<directory>unitary-tests/webservices</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Extensions">
|
||||
<directory>../../env-production/*/test</directory>
|
||||
<directory>../../env-production/*/tests/php-unit-tests</directory>
|
||||
</testsuite>
|
||||
|
||||
<!-- Integration tests -->
|
||||
<testsuite name="Integration">
|
||||
<directory>integration-tests</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Service">
|
||||
<directory>service</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<!-- Code coverage white list -->
|
||||
<filter>
|
||||
<whitelist>
|
||||
<file>../../core/apc-emulation.php</file>
|
||||
<file>../../core/ormlinkset.class.inc.php</file>
|
||||
<file>../../datamodels/2.x/itop-tickets/main.itop-tickets.php</file>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-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
|
||||
* along with iTop. If not, see <http: *www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use iTopDesignFormat;
|
||||
|
||||
|
||||
/**
|
||||
* Class iTopDesignFormatChecklistTest
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*/
|
||||
class SetupCssIntegrityChecklistTest extends ItopTestCase
|
||||
{
|
||||
public function testSetupCssIntegrity()
|
||||
{
|
||||
$sSetupCssPath = APPROOT.'css/setup.css';
|
||||
$sSetupCssContent = file_get_contents($sSetupCssPath);
|
||||
$this->assertGreaterThan(4000, strlen($sSetupCssContent), "Test if the resulting file $sSetupCssPath is long enough, the value is totally arbitrary (at the time of the writing the file is 5660o long");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use iTopDesignFormat;
|
||||
use PHPUnit\Exception;
|
||||
|
||||
|
||||
/**
|
||||
* Class iTopDesignFormatChecklistTest
|
||||
* Ticket 3053 - Check XML conversion methods
|
||||
*
|
||||
* @covers iTopDesignFormat
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class TestForITopDesignFormatClass extends ItopTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->RequireOnceItopFile('setup/modelfactory.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/itopdesignformat.class.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* release checklist step: make sure we have datamodel conversion functions for new iTop version
|
||||
*/
|
||||
public function testCurrentVersion_DataModelConversionFunctions()
|
||||
{
|
||||
$aErrors = [];
|
||||
$aDatamodelCurrentVersions = array();
|
||||
$aDataModelFiles = $this->GetDataModelFiles(APPROOT.'/datamodels');
|
||||
|
||||
//retrieve current XML version in datamoldels files
|
||||
foreach ($aDataModelFiles as $sDataModelFile)
|
||||
{
|
||||
if (preg_match('/itop_design .* version="([\d\.]*)"/', file_get_contents($sDataModelFile), $aMatches))
|
||||
{
|
||||
$sVersion = $aMatches[1];
|
||||
if (!array_key_exists($sVersion, $aDatamodelCurrentVersions))
|
||||
{
|
||||
if (trim($sVersion) === '')
|
||||
{
|
||||
$aErrors[] = "cannot retrieve itop_design datamodel version in $sDataModelFile:1";
|
||||
continue;
|
||||
}
|
||||
|
||||
$aDatamodelCurrentVersions[$sVersion] = $sVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//make sure there is only one found
|
||||
$this->assertTrue(is_array($aDatamodelCurrentVersions));
|
||||
|
||||
$sFirstVersion = array_keys(iTopDesignFormat::$aVersions)[0];
|
||||
$sLatestVersion = array_keys(iTopDesignFormat::$aVersions)[count(iTopDesignFormat::$aVersions)-1];
|
||||
foreach ($aDatamodelCurrentVersions as $sCurrentVersion)
|
||||
{
|
||||
try{
|
||||
//check we have migration function from current version to previous
|
||||
$this->CheckCondition(array_key_exists($sCurrentVersion, iTopDesignFormat::$aVersions), "Missing $sCurrentVersion conversion functions in iTopDesignFormat.");
|
||||
$aCurrentVersionInfo = iTopDesignFormat::$aVersions[$sCurrentVersion];
|
||||
$this->CheckCondition(is_array($aCurrentVersionInfo), "Wrong $sCurrentVersion config in iTopDesignFormat.");
|
||||
$this->CheckCondition(array_key_exists('previous', $aCurrentVersionInfo), "Missing previous for $sCurrentVersion config in iTopDesignFormat.");
|
||||
$this->TestDefinedFunction($aCurrentVersionInfo, 'go_to_next', $sCurrentVersion, ($sCurrentVersion=== $sLatestVersion));
|
||||
$this->TestDefinedFunction($aCurrentVersionInfo, 'go_to_previous', $sCurrentVersion, ($sCurrentVersion==='1.0'));
|
||||
|
||||
//check we have migration function from N-1 version to current one
|
||||
if (($sCurrentVersion!=='1.0')) {
|
||||
$sPreviousVersion = $aCurrentVersionInfo['previous'];
|
||||
$this->CheckCondition(array_key_exists($sPreviousVersion, iTopDesignFormat::$aVersions),
|
||||
"$sCurrentVersion: Missing $sPreviousVersion config in iTopDesignFormat.");
|
||||
$aPreviousVersionInfo = iTopDesignFormat::$aVersions[$sPreviousVersion];
|
||||
$this->CheckCondition(is_array($aPreviousVersionInfo),
|
||||
"$sCurrentVersion: wrong $sPreviousVersion config in iTopDesignFormat.");
|
||||
$this->CheckCondition(array_key_exists('previous', $aPreviousVersionInfo),
|
||||
"$sCurrentVersion: Missing previous for $sPreviousVersion config in iTopDesignFormat.");
|
||||
$this->TestDefinedFunction($aPreviousVersionInfo, 'go_to_previous', $sPreviousVersion, ($sPreviousVersion === '1.0'));
|
||||
$this->TestDefinedFunction($aPreviousVersionInfo, 'go_to_next', $sPreviousVersion, ($sPreviousVersion === $sLatestVersion));
|
||||
}
|
||||
|
||||
//check we have migration function from current version to next one
|
||||
if (($sCurrentVersion!== $sLatestVersion)) {
|
||||
$sNextVersion = $aCurrentVersionInfo['next'];
|
||||
$this->CheckCondition(array_key_exists($sNextVersion, iTopDesignFormat::$aVersions),
|
||||
"$sCurrentVersion: Missing $sNextVersion config in iTopDesignFormat.");
|
||||
$aNextVersionInfo = iTopDesignFormat::$aVersions[$sNextVersion];
|
||||
$this->CheckCondition(is_array($aNextVersionInfo),
|
||||
"$sCurrentVersion: wrong $sNextVersion config in iTopDesignFormat.");
|
||||
$this->CheckCondition(array_key_exists('previous', $aNextVersionInfo),
|
||||
"$sCurrentVersion: Missing previous for $sNextVersion config in iTopDesignFormat.");
|
||||
$this->TestDefinedFunction($aNextVersionInfo, 'go_to_previous', $sNextVersion, ($sNextVersion === '1.0'));
|
||||
$this->TestDefinedFunction($aNextVersionInfo, 'go_to_next', $sNextVersion, ($sNextVersion === $sLatestVersion));
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$aErrors[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aErrors)!=0)
|
||||
{
|
||||
$sMsg = "Issue with conversion functions:\n";
|
||||
$sMsg .= implode("\n", $aErrors);
|
||||
$this->fail($sMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
private function CheckCondition($bCondition, $sMsg)
|
||||
{
|
||||
if ($bCondition === false)
|
||||
{
|
||||
throw new \Exception($sMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private function TestDefinedFunction($aCurrentVersionInfo, $sFunctionKey, $sVersion, $bNullFunction=false)
|
||||
{
|
||||
$sInfo = json_encode($aCurrentVersionInfo, true);
|
||||
$this->CheckCondition(array_key_exists($sFunctionKey, $aCurrentVersionInfo), "Missing $sFunctionKey in $sVersion config in iTopDesignFormat: " . $sInfo);
|
||||
//echo $aCurrentVersionInfo[$sFunctionKey].'\n';
|
||||
if ($bNullFunction === false)
|
||||
{
|
||||
$oReflectionClass = new \ReflectionClass(iTopDesignFormat::class);
|
||||
$this->CheckCondition($oReflectionClass->hasMethod($aCurrentVersionInfo[$sFunctionKey]), "wrong go_to_previous function '".$aCurrentVersionInfo[$sFunctionKey]."'' for $sVersion config in iTopDesignFormat." . $sInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->CheckCondition(is_null($aCurrentVersionInfo[$sFunctionKey]), "$sVersion $sFunctionKey function should be null");
|
||||
}
|
||||
}
|
||||
|
||||
public function GetDataModelFiles($sFolder)
|
||||
{
|
||||
$aDataModelFiles = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$aDataModelFiles = array_merge($aDataModelFiles, $this->GetDataModelFiles($sPath));
|
||||
}
|
||||
else if (preg_match("/datamodel\..*\.xml/", basename($sPath)))
|
||||
{
|
||||
$aDataModelFiles[] = $sPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aDataModelFiles;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\ReleaseChecklist;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.7.2 N°3060 / N°3061 Automatically check the installation.xml consistency
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Setup
|
||||
*/
|
||||
class iTopModuleXmlInstallationChecklistTest extends ItopTestCase
|
||||
{
|
||||
/**
|
||||
* make sure installation.xml is provided and respects XML format
|
||||
*/
|
||||
public function testInstallationXmlFormat()
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT . 'datamodels/2.x/installation.xml';
|
||||
$this->assertTrue(is_file($sInstallationXmlPath), "$sInstallationXmlPath does not exist");
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
try{
|
||||
$doc->loadxml(file_get_contents($sInstallationXmlPath));
|
||||
}
|
||||
catch(\Exception $e)
|
||||
{
|
||||
$this->assertFalse(true, "$sInstallationXmlPath is not a valid XML content: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure installation.xml includes all packaged modules
|
||||
*/
|
||||
public function testAllModuleAreIncludedInInstallationXml()
|
||||
{
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/2.x/installation.xml';
|
||||
if (!is_file($sInstallationXmlPath)) {
|
||||
$sInstallationXmlPath = APPROOT.'datamodels/1.x/installation.xml';
|
||||
}
|
||||
$this->assertTrue(is_file($sInstallationXmlPath), "$sInstallationXmlPath does not exist");
|
||||
|
||||
$sInstallationXmlContent = file_get_contents($sInstallationXmlPath);
|
||||
preg_match_all("|<module>(.*)</module>|", $sInstallationXmlContent, $aMatches);
|
||||
$aDeclaredModules = [];
|
||||
if (!empty($aMatches)) {
|
||||
foreach ($aMatches[1] as $sModule) {
|
||||
if (!array_key_exists($sModule, $aDeclaredModules)) {
|
||||
$aDeclaredModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertArraySubset(
|
||||
$this->GetFilteredModulesFromDatamodels(APPROOT.'/datamodels'),
|
||||
$aDeclaredModules,
|
||||
false,
|
||||
"{$sInstallationXmlPath} does not list all modules in /datamodels ! List of modules in installation.xml:\n ".var_export($aDeclaredModules, true)
|
||||
);
|
||||
|
||||
$aModulesFromDatamodels = $this->GetAllModules(APPROOT.'/datamodels');
|
||||
$this->assertArraySubset(
|
||||
$aDeclaredModules,
|
||||
$aModulesFromDatamodels,
|
||||
false,
|
||||
"Not all modules are contained in {$sInstallationXmlPath}. List of modules in /datamodels:\n ".var_export($aModulesFromDatamodels, true)
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFilteredModulesFromDatamodels($sFolder)
|
||||
{
|
||||
$aExcludedModules = ['authent-external', 'authent-ldap'];
|
||||
$aModules = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$aModules = array_merge($aModules, $this->GetFilteredModulesFromDatamodels($sPath));
|
||||
}
|
||||
else if (preg_match("/module\..*\.php/", basename($sPath)))
|
||||
{
|
||||
$sModulePhpContent = file_get_contents($sPath);
|
||||
if (strpos($sModulePhpContent, "SetupWebPage::AddModule")!==false
|
||||
&& strpos($sModulePhpContent, "'mandatory' => true")===false)
|
||||
{
|
||||
//filter modules autoselected due to below condition
|
||||
if (strpos($sModulePhpContent, "'mandatory' => false")!==false
|
||||
&& strpos($sModulePhpContent, "'visible' => false")!==false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$sModule = basename(dirname($sPath));
|
||||
if (in_array($sModule, $aExcludedModules))// || $sModule === 'authent-ldap')
|
||||
{
|
||||
//hardcode this condition to make sure test is OK (CI context) + added a ticket to work/investigate why it is failed for these 2 cases (itop dev context)
|
||||
continue;
|
||||
}
|
||||
|
||||
$aModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aModules;
|
||||
}
|
||||
|
||||
public function GetAllModules($sFolder)
|
||||
{
|
||||
$aModules = array();
|
||||
if (is_dir($sFolder))
|
||||
{
|
||||
foreach (glob($sFolder."/*") as $sPath)
|
||||
{
|
||||
if (is_dir($sPath))
|
||||
{
|
||||
/** @noinspection SlowArrayOperationsInLoopInspection */
|
||||
$aModules = array_merge($aModules, $this->GetAllModules($sPath));
|
||||
}
|
||||
else if (preg_match("/module\..*\.php/", basename($sPath)))
|
||||
{
|
||||
$sModulePhpContent = file_get_contents($sPath);
|
||||
if (strpos($sModulePhpContent, "SetupWebPage::AddModule")!==false)
|
||||
{
|
||||
$sModule = basename(dirname($sPath));
|
||||
$aModules[$sModule] = $sModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aModules;
|
||||
}
|
||||
}
|
||||
43
tests/php-unit-tests/postbuild_integration.xml.dist
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.5/phpunit.xsd"
|
||||
bootstrap="unittestautoload.php"
|
||||
backupGlobals="true"
|
||||
colors="true"
|
||||
columns="120"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
stopOnIncomplete="false"
|
||||
stopOnRisky="false"
|
||||
stopOnSkipped="false"
|
||||
verbose="true"
|
||||
>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL"/>
|
||||
<ini name="display_errors" value="On"/>
|
||||
<ini name="log_errors" value="On"/>
|
||||
<ini name="html_errors" value="Off"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="PostBuildIntegration">
|
||||
<directory>post-build-integration-tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<!-- Code coverage white list -->
|
||||
<filter>
|
||||
<whitelist>
|
||||
<file>../../../core/apc-emulation.php</file>
|
||||
<file>../../../core/ormlinkset.class.inc.php</file>
|
||||
<file>../../../datamodels/2.x/itop-tickets/main.itop-tickets.php</file>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2022 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
class DatamodelsXmlFilesTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
require_once APPROOT.'.make/release/update.classes.inc.php';
|
||||
require_once APPROOT.'setup/itopdesignformat.class.inc.php';
|
||||
}
|
||||
|
||||
public function testGetDesignVersionToSet() {
|
||||
$sVersionFor10 = $this->InvokeNonPublicStaticMethod(DatamodelsXmlFiles::class, 'GetDesignVersionToSet', ['1.0']);
|
||||
$this->assertNull($sVersionFor10);
|
||||
$sVersionFor26 = $this->InvokeNonPublicStaticMethod(DatamodelsXmlFiles::class, 'GetDesignVersionToSet', ['2.6']);
|
||||
$this->assertNull($sVersionFor26);
|
||||
$sVersionFor27 = $this->InvokeNonPublicStaticMethod(DatamodelsXmlFiles::class, 'GetDesignVersionToSet', ['2.7']);
|
||||
$this->assertNull($sVersionFor27);
|
||||
|
||||
$sPreviousDesignVersion = iTopDesignFormat::GetPreviousDesignVersion(ITOP_DESIGN_LATEST_VERSION);
|
||||
$sVersionForLatest = $this->InvokeNonPublicStaticMethod(DatamodelsXmlFiles::class, 'GetDesignVersionToSet', [ITOP_DESIGN_LATEST_VERSION]);
|
||||
$this->assertNotNull($sVersionForLatest);
|
||||
$this->assertSame($sPreviousDesignVersion, $sVersionForLatest);
|
||||
}
|
||||
|
||||
public function testAllItopXmlFilesCovered()
|
||||
{
|
||||
$oXmlUpdate = new DatamodelsXmlFiles();
|
||||
$aITopDesignFilesFromScript = $oXmlUpdate->GetFiles();
|
||||
$aITopDesignFilesFromScript = array_map(function ($value) {
|
||||
return str_replace('\\', '/', $value);
|
||||
}, $aITopDesignFilesFromScript);
|
||||
|
||||
$oDirectoryIterator = new RecursiveDirectoryIterator(APPROOT,
|
||||
FilesystemIterator::KEY_AS_PATHNAME
|
||||
| FilesystemIterator::CURRENT_AS_FILEINFO
|
||||
| FilesystemIterator::SKIP_DOTS
|
||||
| FilesystemIterator::UNIX_PATHS
|
||||
);
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
$oDirectoryIterator,
|
||||
RecursiveIteratorIterator::CHILD_FIRST);
|
||||
$oITopDesignIterator = new ITopDesignFilter($iterator);
|
||||
$aITopDesignFilesFromDisk = iterator_to_array($oITopDesignIterator);
|
||||
$aITopDesignFilesFromDisk = array_map(function ($file) {
|
||||
return str_replace('\\', '/', $file->getPathname());
|
||||
}, $aITopDesignFilesFromDisk);
|
||||
|
||||
sort($aITopDesignFilesFromScript);
|
||||
sort($aITopDesignFilesFromDisk);
|
||||
$this->assertEquals($aITopDesignFilesFromDisk, $aITopDesignFilesFromScript, 'The XML files update script is not targeting some iTop design files ! '.DatamodelsXmlFiles::class.' must be updated !');
|
||||
}
|
||||
}
|
||||
|
||||
class ITopDesignFilter extends FilterIterator
|
||||
{
|
||||
const DIRECTORIES_TO_EXCLUDE = [
|
||||
// iTop repo excluded dirs
|
||||
'.doc',
|
||||
'.git',
|
||||
'.idea',
|
||||
'data/', // trailing slash to avoid confusion with datamodels dir
|
||||
'env-',
|
||||
'extensions',
|
||||
'test',
|
||||
|
||||
// clones specificities
|
||||
'toolkit',
|
||||
'toolkit-pro',
|
||||
'.delta',
|
||||
'.hacks',
|
||||
];
|
||||
private $aDirToExcludeFullPath;
|
||||
|
||||
public function __construct($iterator)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->aDirToExcludeFullPath = array_map(function ($value) {
|
||||
$sRawDir = APPROOT.$value;
|
||||
|
||||
return str_replace('\\', '/', $sRawDir);
|
||||
}, self::DIRECTORIES_TO_EXCLUDE);
|
||||
}
|
||||
|
||||
public function accept()
|
||||
{
|
||||
$file = $this->current();
|
||||
|
||||
if ($file->isDir()) {
|
||||
return false;
|
||||
}
|
||||
$sFilePath = str_replace('\\', '/', $file->getPathname());
|
||||
if (substr($sFilePath, -4) !== '.xml') {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->aDirToExcludeFullPath as $sDirToExclude) {
|
||||
if (strpos($sFilePath, $sDirToExclude) === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $this->IsITopDesignFile($sFilePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function IsITopDesignFile($sFilePath)
|
||||
{
|
||||
$oXmlFile = fopen($sFilePath, 'r');
|
||||
if (false === $oXmlFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sLine1 = fgets($oXmlFile);
|
||||
if (strpos($sLine1, '<?xml version="1.0" encoding="UTF-8"?>') !== 0) {
|
||||
fclose($oXmlFile);
|
||||
|
||||
return false;
|
||||
}
|
||||
$sLine2 = fgets($oXmlFile);
|
||||
$sITopDesignRootNode = '<itop_design ';
|
||||
if (strpos($sLine2, $sITopDesignRootNode) !== 0) {
|
||||
fclose($oXmlFile);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose($oXmlFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?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\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @covers utils
|
||||
*/
|
||||
class DashboardLayoutTest extends ItopTestCase
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetDashletCoordinatesProvider()
|
||||
{
|
||||
return array(
|
||||
'OneColLayout-Cell0' => array('DashboardLayoutOneCol', 0, array(0, 0)),
|
||||
'OneColLayout-Cell1' => array('DashboardLayoutOneCol', 1, array(0, 1)),
|
||||
'TwoColsLayout-Cell0' => array('DashboardLayoutTwoCols', 0, array(0, 0)),
|
||||
'TwoColsLayout-Cell1' => array('DashboardLayoutTwoCols', 1, array(1, 0)),
|
||||
'TwoColsLayout-Cell2' => array('DashboardLayoutTwoCols', 2, array(0, 1)),
|
||||
'TwoColsLayout-Cell3' => array('DashboardLayoutTwoCols', 3, array(1, 1)),
|
||||
'ThreeColsLayout-Cell0' => array('DashboardLayoutThreeCols', 0, array(0, 0)),
|
||||
'ThreeColsLayout-Cell1' => array('DashboardLayoutThreeCols', 1, array(1, 0)),
|
||||
'ThreeColsLayout-Cell2' => array('DashboardLayoutThreeCols', 2, array(2, 0)),
|
||||
'ThreeColsLayout-Cell3' => array('DashboardLayoutThreeCols', 3, array(0, 1)),
|
||||
'ThreeColsLayout-Cell4' => array('DashboardLayoutThreeCols', 4, array(1, 1)),
|
||||
'ThreeColsLayout-Cell5' => array('DashboardLayoutThreeCols', 5, array(2, 1)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDashboardLayoutClass
|
||||
* @param int $iCellIdx
|
||||
* @param array $aExpectedCoordinates
|
||||
* @dataProvider GetDashletCoordinatesProvider
|
||||
* @since N°2735
|
||||
*/
|
||||
public function testGetDashletCoordinates($sDashboardLayoutClass, $iCellIdx, $aExpectedCoordinates)
|
||||
{
|
||||
$oDashboardLayout = new $sDashboardLayoutClass();
|
||||
$aDashletCoordinates = $oDashboardLayout->GetDashletCoordinates($iCellIdx);
|
||||
|
||||
$this->assertEquals($aExpectedCoordinates,$aDashletCoordinates);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use utils;
|
||||
|
||||
class SCSSCompilationTest extends ItopTestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @dataProvider CompileDefaultThemesProvider
|
||||
* @doesNotPerformAssertions
|
||||
* @return void
|
||||
*/
|
||||
public function testCompileDefaultThemes($sSassRelPath, $aImportRelPaths)
|
||||
{
|
||||
$aImportPaths = [];
|
||||
foreach ($aImportRelPaths as $sPath) {
|
||||
$aImportPaths[] = APPROOT.$sPath;
|
||||
}
|
||||
|
||||
$sSassPath = APPROOT.$sSassRelPath;
|
||||
$sCSS = utils::CompileCSSFromSASS(file_get_contents($sSassPath), $aImportPaths);
|
||||
|
||||
$this->debug($sCSS);
|
||||
}
|
||||
|
||||
public function CompileDefaultThemesProvider()
|
||||
{
|
||||
return [
|
||||
'console' => ['css/backoffice/main.scss', ['css/backoffice/']],
|
||||
'portal' => ['env-production/itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss', ['env-production//itop-portal-base/portal/public/css/']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class SessionTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Session::$bAllowCLI = true;
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
Session::$bAllowCLI = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Combodo\iTop\Application\Helper\Session::Start
|
||||
*/
|
||||
public function testStart()
|
||||
{
|
||||
$this->assertNull(Session::$iSessionId);
|
||||
Session::Start();
|
||||
$this->assertNotNull(Session::$iSessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Combodo\iTop\Application\Helper\Session::WriteClose
|
||||
*/
|
||||
public function testWriteClose()
|
||||
{
|
||||
$this->assertNull(Session::$iSessionId);
|
||||
Session::Start();
|
||||
$this->assertNotNull(Session::$iSessionId);
|
||||
Session::WriteClose();
|
||||
$_SESSION['test'] = 'OK';
|
||||
Session::Start();
|
||||
$this->assertArrayNotHasKey('test', $_SESSION);
|
||||
}
|
||||
|
||||
public function testReopenningSession()
|
||||
{
|
||||
Session::Start();
|
||||
$_SESSION['test'] = 'OK';
|
||||
Session::WriteClose();
|
||||
unset($_SESSION['test']);
|
||||
Session::Start();
|
||||
$this->assertEquals('OK', $_SESSION['test']);
|
||||
}
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
Session::Start();
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
Session::WriteClose();
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
unset($_SESSION['test']);
|
||||
Session::Start();
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
}
|
||||
|
||||
public function testSetArray()
|
||||
{
|
||||
Session::Start();
|
||||
Session::Set(['test1', 'test2', 'test3'], 'OK');
|
||||
$this->assertEquals('OK', Session::Get(['test1', 'test2', 'test3']));
|
||||
Session::WriteClose();
|
||||
$this->assertEquals('OK', Session::Get(['test1', 'test2', 'test3']));
|
||||
unset($_SESSION['test1']);
|
||||
Session::Start();
|
||||
$this->assertEquals('OK', Session::Get(['test1', 'test2', 'test3']));
|
||||
}
|
||||
|
||||
public function testSetOnClosedSession()
|
||||
{
|
||||
Session::Start();
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
Session::WriteClose();
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
unset($_SESSION['test']);
|
||||
Session::Start();
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
}
|
||||
|
||||
|
||||
public function testIsSet()
|
||||
{
|
||||
$this->assertFalse(Session::IsSet('test'));
|
||||
Session::Start();
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertTrue(Session::IsSet('test'));
|
||||
Session::Set(['test1', 'test2', 'test3'], 'OK');
|
||||
$this->assertTrue(Session::IsSet('test1'));
|
||||
$this->assertTrue(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
Session::WriteClose();
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
Session::Start();
|
||||
$this->assertNull(Session::Get('test'));
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertEquals('OK', Session::Get('test'));
|
||||
Session::WriteClose();
|
||||
}
|
||||
|
||||
public function testUnset()
|
||||
{
|
||||
Session::Start();
|
||||
Session::Unset('test');
|
||||
$this->assertFalse(Session::IsSet('test'));
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertTrue(Session::IsSet('test'));
|
||||
Session::Unset('test');
|
||||
$this->assertFalse(Session::IsSet('test'));
|
||||
Session::Set('test', 'OK');
|
||||
$this->assertTrue(Session::IsSet('test'));
|
||||
Session::Set(['test1', 'test2', 'test3'], 'OK');
|
||||
$this->assertTrue(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
Session::Unset(['test1', 'test2', 'test3']);
|
||||
$this->assertFalse(Session::IsSet(['test1', 'test2', 'test3']));
|
||||
}
|
||||
|
||||
public function testRegenerateId()
|
||||
{
|
||||
Session::Start();
|
||||
$iPrevSessionId = Session::$iSessionId;
|
||||
Session::RegenerateId();
|
||||
//$this->assertFalse(Session::IsSet('test'));
|
||||
$this->assertNotEquals($iPrevSessionId, Session::$iSessionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use FindStylesheetObject;
|
||||
use ThemeHandler;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
* @covers ThemeHandler
|
||||
*/
|
||||
class ThemeHandlerTest extends ItopTestCase
|
||||
{
|
||||
const PATTERN = '|\\\/var[^"]+testimages|';
|
||||
|
||||
private $oCompileCSSServiceMock;
|
||||
private $sCompiledThemesDirAbsPath;
|
||||
private $sCssAbsPath;
|
||||
private $sDmCssAbsPath;
|
||||
private $sJsonThemeParamFile;
|
||||
private $sTmpDir;
|
||||
private $aDirsToCleanup= [];
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->RequireOnceItopFile('application/themehandler.class.inc.php');
|
||||
$this->RequireOnceItopFile('setup/modelfactory.class.inc.php');
|
||||
$this->RequireOnceUnitTestFile('../setup/SubMFCompiler.php');
|
||||
|
||||
$this->oCompileCSSServiceMock = $this->createMock('CompileCSSService');
|
||||
ThemeHandler::mockCompileCSSService($this->oCompileCSSServiceMock);
|
||||
|
||||
$this->sTmpDir = $this->CreateTmpdir().'/';
|
||||
$this->aDirsToCleanup[] = $this->sTmpDir;
|
||||
|
||||
|
||||
$this->sCompiledThemesDirAbsPath = $this->sTmpDir."branding/themes/";
|
||||
$this->recurseMkdir($this->sCompiledThemesDirAbsPath."basque-red/");
|
||||
$this->sCssAbsPath = $this->sCompiledThemesDirAbsPath.'basque-red/main.css';
|
||||
$this->sDmCssAbsPath = $this->sCompiledThemesDirAbsPath.'datamodel-compiled-scss-rules.scss';
|
||||
$this->sJsonThemeParamFile = $this->sCompiledThemesDirAbsPath.'basque-red/theme-parameters.json';
|
||||
$this->RecurseCopy(APPROOT."/tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css", $this->sTmpDir."/branding/css");
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
foreach ($this->aDirsToCleanup as $sDir)
|
||||
{
|
||||
echo $sDir;
|
||||
$this->RecurseRmdir($sDir);
|
||||
}
|
||||
}
|
||||
|
||||
function KeepSignatureDiff($sSignature1, $sSignature2) : string {
|
||||
$aSignature1 = json_decode($sSignature1, true);
|
||||
$aSignature2 = json_decode($sSignature2, true);
|
||||
|
||||
$aDiffOuput = [];
|
||||
foreach ($aSignature1 as $sKey => $oVal1){
|
||||
if (is_array($oVal1) && ! empty($oVal1)){
|
||||
$aCurrentDiffVal = [];
|
||||
$oVal2 = $aSignature2[$sKey];
|
||||
if (0 != sizeof($oVal1)){
|
||||
foreach ($oVal1 as $sKey1 => $sVal1){
|
||||
if (! array_key_exists($sKey1, $oVal2)){
|
||||
$aCurrentDiffVal[$sKey1] = "Missing";
|
||||
} else if ($sVal1 !== $oVal2[$sKey1]) {
|
||||
$aCurrentDiffVal[$sKey1] = "expected:$sVal1 | actual:" . $oVal2[$sKey1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! empty($oVal2)){
|
||||
foreach ($oVal2 as $sKey2 => $sVal2){
|
||||
if (! array_key_exists($sKey2, $oVal1)){
|
||||
$aCurrentDiffVal[$sKey1] = "Missing";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! empty($aCurrentDiffVal)){
|
||||
$aDiffOuput[$sKey] = $aCurrentDiffVal;
|
||||
}
|
||||
} else if ($oVal1 !== $aSignature2[$sKey]){
|
||||
$aDiffOuput[$sKey] = "expected:$oVal1 | actual:$aSignature2[$sKey]";
|
||||
}
|
||||
}
|
||||
|
||||
return json_encode($aDiffOuput, true);
|
||||
}
|
||||
|
||||
function recurseMkdir($dir)
|
||||
{
|
||||
if (is_dir($dir))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$sParentDir = dirname($dir);
|
||||
if (!$this->recurseMkdir($sParentDir))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return @mkdir($dir);
|
||||
}
|
||||
|
||||
public function testGetSignatureWithFileWithoutSignature()
|
||||
{
|
||||
$sTmpFile = tempnam(sys_get_temp_dir(), "sig");
|
||||
file_put_contents($sTmpFile,"ffff");
|
||||
$this->assertEquals("", ThemeHandler::GetSignature($sTmpFile));
|
||||
}
|
||||
|
||||
public function testGetSignature()
|
||||
{
|
||||
$sSig = ThemeHandler::GetSignature(APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main.css');
|
||||
$sExpectedSig=<<<JSON
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"variable_imports":{"css-variables":"3c3f5adf98b9dbf893658314436c4b93"},"images":{"css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","images\/calendar.png":"ab56e59af3c96ca661821257d376465e","images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","images\/delete.png":"93c047549c31a270a037840277cf59d3","images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5"},"utility_imports":[]}
|
||||
JSON;
|
||||
|
||||
$this->assertEquals($sExpectedSig, $sSig);
|
||||
}
|
||||
|
||||
public function testGetVarSignature()
|
||||
{
|
||||
$sSignature=<<<JSON
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"css-variables":"934888ebb4991d4c76555be6b6d1d5cc","jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"variable_imports":[],"utility_imports":[]}
|
||||
JSON;
|
||||
$var_sig = ThemeHandler::GetVarSignature($sSignature);
|
||||
|
||||
$this->assertEquals("37c31105548fce44fecca5cb34e455c9",$var_sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $readFromParamAttributeFromJson
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @dataProvider CompileThemesProviderWithoutCss
|
||||
*/
|
||||
public function testCompileThemeWithoutCssFile_FocusOnParamAttribute($readFromParamAttributeFromJson=false)
|
||||
{
|
||||
$sExpectJsonFilePath = APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
|
||||
$sExpectedThemeParamJson = file_get_contents($sExpectJsonFilePath);
|
||||
$aThemeParameters = json_decode($sExpectedThemeParamJson, true);
|
||||
if (is_file($this->sJsonThemeParamFile))
|
||||
{
|
||||
unlink($this->sJsonThemeParamFile);
|
||||
}
|
||||
if (is_file($this->sCssAbsPath))
|
||||
{
|
||||
unlink($this->sCssAbsPath);
|
||||
}
|
||||
|
||||
$this->oCompileCSSServiceMock->expects($this->exactly(1))
|
||||
->method("CompileCSSFromSASS")
|
||||
->willReturn("====CSSCOMPILEDCONTENT====");
|
||||
|
||||
if($readFromParamAttributeFromJson)
|
||||
{
|
||||
copy($sExpectJsonFilePath, $this->sJsonThemeParamFile);
|
||||
$this->assertTrue(ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", null, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->assertTrue(ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", $aThemeParameters, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
|
||||
}
|
||||
$this->assertTrue(is_file($this->sCssAbsPath));
|
||||
$this->assertEquals($sExpectedThemeParamJson, file_get_contents($this->sJsonThemeParamFile));
|
||||
$this->assertEquals(file_get_contents(APPROOT . 'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main.css'), file_get_contents($this->sCssAbsPath));
|
||||
}
|
||||
|
||||
public function CompileThemesProviderWithoutCss()
|
||||
{
|
||||
return [
|
||||
"pass ParamAttributes and Save them in Json" => [false],
|
||||
"use them from saved json" => [true]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $ThemeParametersJson
|
||||
*
|
||||
* @param int $CompileCount
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @dataProvider CompileThemesProviderEmptyArray
|
||||
*/
|
||||
public function testCompileThemesEmptyArray($ThemeParametersJson, $CompileCount=0)
|
||||
{
|
||||
$sCssPath = $this->sTmpDir . '/branding/themes/basque-red/main.css';
|
||||
copy(APPROOT . 'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main.css', $sCssPath);
|
||||
|
||||
$this->oCompileCSSServiceMock->expects($this->exactly($CompileCount))
|
||||
->method("CompileCSSFromSASS")
|
||||
->willReturn("====CSSCOMPILEDCONTENT====");
|
||||
|
||||
$this->assertEquals($CompileCount!=0,ThemeHandler::CompileTheme('basque-red', true, "COMPILATIONTIMESTAMP", json_decode($ThemeParametersJson, true), [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
|
||||
}
|
||||
|
||||
public function CompileThemesProviderEmptyArray()
|
||||
{
|
||||
$aEmptyImports = '{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"utility_imports":[],"variable_imports":[],"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/DO_NOT_CHANGE.jqueryui.scss","main":"..\/css\/DO_NOT_CHANGE.light-grey.scss"}}';
|
||||
$aEmptyStyleSheets='{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"utility_imports":{"css-variables":"..\/css\/DO_NOT_CHANGE.css-variables.scss"},"variable_imports":[],"stylesheets":[]}';
|
||||
$aEmptyVars='{"variables":[],"utility_imports":{"css-variables":"..\/css\/DO_NOT_CHANGE.css-variables.scss"},"variable_imports":[],"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/DO_NOT_CHANGE.jqueryui.scss","main":"..\/css\/DO_NOT_CHANGE.light-grey.scss"}}';
|
||||
return [
|
||||
"empty imports" => [$aEmptyImports],
|
||||
"empty styles" => [$aEmptyStyleSheets],
|
||||
"empty vars" => [$aEmptyVars, 1],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function CompileThemesProvider()
|
||||
{
|
||||
$sModifiedVariableThemeParameterJson='{"variables":{"brand-primary1":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"variable_imports":{"css-variables":"..\/css\/DO_NOT_CHANGE.css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/DO_NOT_CHANGE.jqueryui.scss","main":"..\/css\/DO_NOT_CHANGE.light-grey.scss"},"utility_imports":[]}';
|
||||
$sInitialThemeParamJson='{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"variable_imports":{"css-variables":"..\/css\/DO_NOT_CHANGE.css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/DO_NOT_CHANGE.jqueryui.scss","main":"..\/css\/DO_NOT_CHANGE.light-grey.scss"},"utility_imports":[]}';
|
||||
$sImportFilePath = '/branding/css/DO_NOT_CHANGE.css-variables.scss';
|
||||
$sVarChangedMainCssPath="tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_varchanged.css";
|
||||
$sStylesheetMainCssPath="tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_stylesheet.css";
|
||||
$sImageMainCssPath="tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_imagemodified.css";
|
||||
$sImportModifiedMainCssPath="tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_importmodified.css";
|
||||
$sStylesheetFilePath = '/branding/css/DO_NOT_CHANGE.light-grey.scss';
|
||||
$sImageFilePath = 'tests/php-unit-tests/unitary-tests/application/theme-handler/copied/testimages/images/green-header.gif';
|
||||
return [
|
||||
"setup context: variables list modified without any file touched" => [$sModifiedVariableThemeParameterJson, 1,false,false,false,$sImportFilePath, $sVarChangedMainCssPath],
|
||||
"setup context: variables list modified with files touched" => [$sModifiedVariableThemeParameterJson, 1,false,true,false,$sImportFilePath, $sVarChangedMainCssPath, false],
|
||||
"itop page/theme loading; variables list modified without any file touched" => [$sModifiedVariableThemeParameterJson, 0,false,false,false,$sImportFilePath, $sVarChangedMainCssPath, false],
|
||||
//imports
|
||||
"import file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sImportFilePath],
|
||||
"import file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sImportFilePath],
|
||||
"import file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sImportFilePath, $sImportModifiedMainCssPath],
|
||||
//stylesheets
|
||||
"stylesheets file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sStylesheetFilePath],
|
||||
"stylesheets file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sStylesheetFilePath],
|
||||
"stylesheets file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sStylesheetFilePath, $sStylesheetMainCssPath],
|
||||
//images
|
||||
"image file missing" => [$sInitialThemeParamJson, 0, true, false, false, $sImageFilePath],
|
||||
"image file touched" => [$sInitialThemeParamJson, 0, false, true, false, $sImageFilePath],
|
||||
"image file modified" => [$sInitialThemeParamJson, 1, false, false, true, $sImageFilePath, $sImageMainCssPath],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $ThemeParametersJson
|
||||
* @param int $iCompileCSSFromSASSCount
|
||||
* @param boolean $bMissingFile
|
||||
* @param boolean $bFilesTouchedRecently
|
||||
* @param boolean $bFileMd5sumModified
|
||||
* @param null $sFileToTest
|
||||
* @param null $sExpectedMainCssPath
|
||||
* @param bool $bSetup
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @dataProvider CompileThemesProvider
|
||||
*/
|
||||
public function testCompileThemes($ThemeParametersJson, $iCompileCSSFromSASSCount, $bMissingFile=false, $bFilesTouchedRecently=false, $bFileMd5sumModified=false, $sFileToTest=null, $sExpectedMainCssPath=null, $bSetup=true)
|
||||
{
|
||||
$sAfterReplacementCssVariableMd5sum='';
|
||||
if (is_file($this->sTmpDir.'/'.$sFileToTest))
|
||||
{
|
||||
$sFileToTest = $this->sTmpDir.'/'.$sFileToTest;
|
||||
} else {
|
||||
$sFileToTest = APPROOT.'/'.$sFileToTest;
|
||||
}
|
||||
|
||||
//copy images in test dir
|
||||
$sAbsoluteImagePath = APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/copied/testimages/';
|
||||
$this->recurseMkdir($sAbsoluteImagePath);
|
||||
$this->aDirsToCleanup[] = dirname($sAbsoluteImagePath);
|
||||
$this->RecurseCopy(APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/', $sAbsoluteImagePath);
|
||||
|
||||
//change approot-relative in css-variable to use absolute path
|
||||
$sCssVarPath = $this->sTmpDir."/branding/css/DO_NOT_CHANGE.css-variables.scss";
|
||||
$sBeforeReplacementCssVariableMd5sum = md5_file($sCssVarPath);
|
||||
echo 'BEFORE :'.$sBeforeReplacementCssVariableMd5sum.' '.$sCssVarPath.' ';
|
||||
$sCssVariableContent = file_get_contents($sCssVarPath);
|
||||
$sLine = '$approot-relative: "'.$sAbsoluteImagePath.'" !default;';
|
||||
$sCssVariableContent = preg_replace("/\\\$approot-relative: \"(.*)\"/", $sLine, $sCssVariableContent);
|
||||
file_put_contents($sCssVarPath, $sCssVariableContent);
|
||||
if ($bMissingFile)
|
||||
{
|
||||
$sAfterReplacementCssVariableMd5sum = $sBeforeReplacementCssVariableMd5sum;
|
||||
unlink($sFileToTest);
|
||||
}
|
||||
|
||||
if (is_file($sCssVarPath))
|
||||
{
|
||||
$sAfterReplacementCssVariableMd5sum = md5_file($sCssVarPath);
|
||||
}
|
||||
|
||||
//change cssvar md5sum + image absolute paths
|
||||
$sMainCssContent = file_get_contents(APPROOT."tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_testcompilethemes.css");
|
||||
$sMainCssContent = preg_replace('/MD5SUM/', $sAfterReplacementCssVariableMd5sum, $sMainCssContent);
|
||||
$sReplacement = rtrim($sAbsoluteImagePath, '/');
|
||||
$sReplacement=preg_replace('|\/|', '\/', $sReplacement);
|
||||
$sMainCssContent = preg_replace(static::PATTERN, $sReplacement, $sMainCssContent);
|
||||
$cssPath = $this->sTmpDir . '/branding/themes/basque-red/main.css';
|
||||
echo 'PUT md5sum: '.$sAfterReplacementCssVariableMd5sum.' in '.$cssPath.' ';
|
||||
file_put_contents($cssPath, $sMainCssContent);
|
||||
|
||||
//should be after main.css modification to make sure precompilation check will be performed
|
||||
if ($bFilesTouchedRecently)
|
||||
{
|
||||
sleep(1);
|
||||
touch($sFileToTest);
|
||||
}
|
||||
|
||||
//same: it should be after main.css modification
|
||||
if ($bFileMd5sumModified)
|
||||
{
|
||||
$sMd5sum = md5_file($sFileToTest);
|
||||
echo ' BEFORE touch: ' . $sMd5sum .' ' . $sFileToTest;
|
||||
sleep(1);
|
||||
file_put_contents($sFileToTest, "###\n".file_get_contents($sFileToTest));
|
||||
|
||||
$sMd5sum = md5_file($sFileToTest);
|
||||
echo ' AFTER touch: ' . $sMd5sum .' ' . $sFileToTest;
|
||||
}
|
||||
|
||||
if (is_file($sCssVarPath))
|
||||
{
|
||||
$sAfterReplacementCssVariableMd5sum = md5_file($sCssVarPath);
|
||||
}
|
||||
|
||||
$this->oCompileCSSServiceMock->expects($this->exactly($iCompileCSSFromSASSCount))
|
||||
->method("CompileCSSFromSASS")
|
||||
->willReturn("====CSSCOMPILEDCONTENT====");
|
||||
|
||||
$aThemeParameters = json_decode($ThemeParametersJson, true);
|
||||
$this->assertEquals($iCompileCSSFromSASSCount!=0, ThemeHandler::CompileTheme('basque-red', $bSetup, "COMPILATIONTIMESTAMP", $aThemeParameters, [$this->sTmpDir.'/branding/themes/'], $this->sTmpDir));
|
||||
|
||||
if ($iCompileCSSFromSASSCount==1)
|
||||
{
|
||||
$sExpectedMainCssFile = APPROOT.$sExpectedMainCssPath;
|
||||
if (!is_file($sExpectedMainCssFile)) {
|
||||
$this->assertTrue(false, "Cannot find expected main css file $sExpectedMainCssFile");
|
||||
}
|
||||
|
||||
$aPatterns = [static::PATTERN, '/'.$sBeforeReplacementCssVariableMd5sum.'/'];
|
||||
$aPatterns[] = "/8100523d2e76a70266f3e7110e2fe5fb/";
|
||||
$aPatterns[] = '/MD5SUM/';
|
||||
$aReplacements = [$sReplacement, $sAfterReplacementCssVariableMd5sum];
|
||||
$aReplacements[] = md5(json_encode($aThemeParameters['variables']));
|
||||
$aReplacements[] = $sAfterReplacementCssVariableMd5sum;
|
||||
var_dump($aReplacements);
|
||||
$this->DoInnerJsonValidation($sExpectedMainCssFile, $cssPath, $aPatterns, $aReplacements);
|
||||
}
|
||||
}
|
||||
|
||||
public function DoInnerJsonValidation($sExpectedCssFile, $sActualCssFile, $aPatterns, $aReplacements)
|
||||
{
|
||||
$sActualContent = file_get_contents($sActualCssFile);
|
||||
|
||||
//replace absolute path to fix it in any envt
|
||||
$sExpectedContent = preg_replace($aPatterns, $aReplacements, file_get_contents($sExpectedCssFile));
|
||||
|
||||
//echo($sExpectedContent);
|
||||
if ($sExpectedContent != $sActualContent)
|
||||
{
|
||||
//try to have inner json diff failure
|
||||
/** @var array $aExpectedJson */
|
||||
//replace absolute path to fix it in any envt
|
||||
$sExpectedJson = preg_replace($aPatterns, $aReplacements, ThemeHandler::GetSignature($sExpectedCssFile));
|
||||
$aExpectedJson = json_decode($sExpectedJson, true);
|
||||
/** @var array $aActualJson */
|
||||
$aActualJson = json_decode(ThemeHandler::GetSignature($sActualCssFile), true);
|
||||
echo (ThemeHandler::GetSignature($sActualCssFile));
|
||||
$this->assertEquals($aExpectedJson, $aActualJson, "CSS file dont match ($sExpectedCssFile / $sActualCssFile)");
|
||||
}
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sScssFile
|
||||
*
|
||||
* @dataProvider GetAllUrlFromScssProvider
|
||||
*/
|
||||
public function testGetAllUrlFromScss($sScssFile)
|
||||
{
|
||||
$aIncludedUrls = ThemeHandler::GetAllUrlFromScss(['attr' => "123"],APPROOT.$sScssFile);
|
||||
$this->assertEquals(['approot-relative', 'version', 'version1'], array_values($aIncludedUrls['aMissingVariables']));
|
||||
$this->assertEquals(["attr"=>"123"],
|
||||
$aIncludedUrls['aFoundVariables']);
|
||||
$aExpectedCompletedUrls = [
|
||||
'css/ui-lightness/images/tutu.jpg',
|
||||
"css/ui-lightness/images/tata.jpeg",
|
||||
"css/ui-lightness/images/tete.jpeg?g=123"
|
||||
];
|
||||
$aExpectedToCompleteUrls = [
|
||||
'\'abc/\'+ $approot-relative + "css/ui-lightness/images/toutou.png?v=" + $version',
|
||||
"\$approot-relative + \"css/ui-lightness/images/toto.png?v=\" + \$version",
|
||||
'$approot-relative + \'css/ui-lightness/images/titi.gif?v=\' + $version1',
|
||||
'"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7?v=" + $version',
|
||||
'$approot-relative + \'node_modules/raleway-webfont/fonts/Raleway-Thin.jpeg\'',
|
||||
];
|
||||
|
||||
$aIncludedUrls['aCompleteUrls'];
|
||||
$this->assertEquals($aExpectedCompletedUrls, array_values($aIncludedUrls['aCompleteUrls']));
|
||||
$this->assertEquals($aExpectedToCompleteUrls, array_values($aIncludedUrls['aToCompleteUrls']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetAllUrlFromScssProvider()
|
||||
{
|
||||
return ['test-getimages.scss' => ['tests/php-unit-tests/unitary-tests/application/theme-handler/getimages/test-getimages.scss']];
|
||||
}
|
||||
|
||||
public function testFindMissingVariables()
|
||||
{
|
||||
$sContent = <<< 'SCSS'
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
$approot-relative2: "../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$brand-primary: $combodo-orange !default;
|
||||
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
|
||||
$content-color: #eeeeee !default;
|
||||
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
|
||||
$icons-filter: hue-rotate(0deg) !default;
|
||||
$toto : titi;
|
||||
SCSS;
|
||||
$aMissingVariables = ['gabu', 'toto', 'approot-relative', 'approot-relative2', 'gray-base', 'gray-darker', 'brand-primary', 'brand-primary-lightest', 'content-color', 'default-font-family', 'icons-filter'];
|
||||
list($aMissingVariables, $aFoundVariables) = ThemeHandler::FindMissingVariables(['gabu' => 'zomeu'], $aMissingVariables, ["a" => "b"], $sContent);
|
||||
$aExpectedFoundVariables = [
|
||||
'gabu' => 'zomeu',
|
||||
'toto' => 'titi',
|
||||
'approot-relative' => '../../../../../',
|
||||
'approot-relative2' => '../../',
|
||||
'gray-base' => '#000',
|
||||
'a' => 'b',
|
||||
'content-color' => '#eeeeee',
|
||||
'default-font-family' => 'Trebuchet MS,Tahoma,Verdana,Arial,sans-serif',
|
||||
'icons-filter' => 'hue-rotate(0deg)',
|
||||
'toto' => 'titi',
|
||||
];
|
||||
$this->assertEquals($aExpectedFoundVariables, $aFoundVariables);
|
||||
$this->assertEquals(['gray-darker', 'brand-primary', 'brand-primary-lightest'], $aMissingVariables);
|
||||
}
|
||||
|
||||
public function testGetVariablesFromFile(){
|
||||
$sContent = <<< 'SCSS'
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
$approot-relative2: "../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
$approot-relative3: '../../' ; // relative to env-***/branding/themes/***/main.css
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$brand-primary: $combodo-orange !default;
|
||||
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
|
||||
$content-color: #eeeeee !default;
|
||||
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
|
||||
$icons-filter: hue-rotate(0deg) !default;
|
||||
$toto : titi;
|
||||
SCSS;
|
||||
|
||||
file_put_contents($this->sTmpDir . DIRECTORY_SEPARATOR . 'css-variable.scss', $sContent);
|
||||
$aVariables = ThemeHandler::GetVariablesFromFile(
|
||||
[ 'css-variable.scss' ],
|
||||
[ $this->sTmpDir ]
|
||||
);
|
||||
|
||||
$aExpectedVariables = [
|
||||
'approot-relative' => '../../../../../',
|
||||
'approot-relative2' => '../../',
|
||||
'approot-relative3' => '../../',
|
||||
'gray-base' => '#000',
|
||||
'gray-darker' => 'lighten($gray-base, 13.5%)',
|
||||
'brand-primary' => '$combodo-orange',
|
||||
'brand-primary-lightest' => 'lighten($brand-primary, 15%)',
|
||||
'content-color' => '#eeeeee',
|
||||
'default-font-family' => 'Trebuchet MS,Tahoma,Verdana,Arial,sans-serif',
|
||||
'icons-filter' => 'hue-rotate(0deg)',
|
||||
'toto' => 'titi',
|
||||
];
|
||||
|
||||
$this->assertEquals(
|
||||
$aExpectedVariables,
|
||||
$aVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrlTemplate
|
||||
* @param $aFoundVariables
|
||||
* @param $sExpectedUrl
|
||||
*
|
||||
* @dataProvider ResolveUrlProvider
|
||||
*/
|
||||
public function testResolveUrl($sUrlTemplate, $aFoundVariables, $sExpectedUrl)
|
||||
{
|
||||
$this->assertEquals($sExpectedUrl, ThemeHandler::ResolveUrl($sUrlTemplate, $aFoundVariables));
|
||||
}
|
||||
|
||||
public function ResolveUrlProvider()
|
||||
{
|
||||
return [
|
||||
'XXX + $key1 UNresolved' => ["abc/'+ \$key1", ['key'=>'123'], false],
|
||||
'$key1 + XXX UNresolved' => ["\$key1 + abs", ['key'=>'123'], false],
|
||||
'XXX + $key UNresolved' => ["abc/'+ \$unknownkey", ['key'=>'123'], false],
|
||||
'XXX + $key resolved' => ["abc/'+ \$key", ['key'=>'123'], "abc/123"],
|
||||
'XXX + $key1 resolved' => ["abc/'+ \$key1", ['key1'=>'123'], "abc/123"],
|
||||
'$key + XXX resolved' => ["\$key + \"/abc", ['key'=>'123'], "123/abc"],
|
||||
'XXX + $key + YYY resolved' => ["abc/'+ \$key + '/def", ['key'=>'123'], "abc/123/def"],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetIncludedImages()
|
||||
{
|
||||
$aStylesheetFile=glob($this->sTmpDir."/branding/css/*.scss");
|
||||
$aStylesheetFile[]=$this->sTmpDir."/branding/css/ui-lightness/DO_NOT_CHANGE.jqueryui.scss";
|
||||
$expectJsonFilePath = APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/theme-parameters.json';
|
||||
$expectedThemeParamJson = file_get_contents($expectJsonFilePath);
|
||||
$aThemeParametersVariables = json_decode($expectedThemeParamJson, true);
|
||||
|
||||
//simulate adding timestamp
|
||||
$aThemeParametersVariables['variables']['$version'] = microtime(true);
|
||||
|
||||
$aIncludedImages = ThemeHandler::GetIncludedImages($aThemeParametersVariables['variables'], $aStylesheetFile, "basque-red");
|
||||
|
||||
$aExpectedUris = json_decode(file_get_contents(APPROOT.'tests/php-unit-tests/unitary-tests/application/theme-handler/getimages/expected-getimages.json'), true);
|
||||
$aExpectedImages = [];
|
||||
foreach ($aExpectedUris as $sExpectedUri)
|
||||
{
|
||||
$aExpectedImages[] = ThemeHandler::GetAppRootWithSlashes().$sExpectedUri;
|
||||
}
|
||||
|
||||
$this->assertEquals($aExpectedImages, $aIncludedImages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider FindStylesheetFileProvider
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testFindStylesheetFile(string $sFileToFind, array $aAllImports){
|
||||
$sImportsPath = $this->sTmpDir.'branding/';
|
||||
|
||||
// Windows compat O:)
|
||||
$sFileToFind = $this->UpdateDirSep($sFileToFind);
|
||||
$sImportsPath = $this->UpdateDirSep($sImportsPath);
|
||||
|
||||
$aExpectedAllImports = [];
|
||||
if (count($aAllImports) !== 0) {
|
||||
foreach ($aAllImports as $sFileURI) {
|
||||
$aExpectedAllImports[$sFileURI] = $this->UpdateDirSep($sImportsPath.$sFileURI);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$oFindStylesheetObject = new FindStylesheetObject();
|
||||
ThemeHandler::FindStylesheetFile($sFileToFind, [$sImportsPath], $oFindStylesheetObject);
|
||||
|
||||
$this->assertEquals([$sFileToFind], $oFindStylesheetObject->GetStylesheetFileURIs());
|
||||
$this->assertEquals($aExpectedAllImports, $oFindStylesheetObject->GetImportPaths());
|
||||
$this->assertEquals($sImportsPath.$sFileToFind, $oFindStylesheetObject->GetLastStyleSheetPath());
|
||||
|
||||
$aExpectedAllStylesheetPaths = [];
|
||||
foreach (array_merge([$sFileToFind], $aAllImports) as $sFileUri) {
|
||||
$aExpectedAllStylesheetPaths [] = $this->UpdateDirSep($sImportsPath.$sFileUri);
|
||||
}
|
||||
$this->assertEquals($aExpectedAllStylesheetPaths, $oFindStylesheetObject->GetAllStylesheetPaths());
|
||||
}
|
||||
|
||||
public function FindStylesheetFileProvider()
|
||||
{
|
||||
$sFileToFind3 = 'css/multi_imports.scss';
|
||||
$sFileToFind4 = 'css/included_file1.scss';
|
||||
$sFileToFind5 = 'css/included_scss/included_file2.scss';
|
||||
|
||||
return [
|
||||
"single file to find" => [
|
||||
"sFileToFind" => "css/DO_NOT_CHANGE.light-grey.scss",
|
||||
"aAllImports" => [],
|
||||
],
|
||||
"scss with simple @imports" => [
|
||||
"sFileToFind" => "css/simple_import.scss",
|
||||
"aAllImports" => [$sFileToFind4],
|
||||
],
|
||||
"scss with multi @imports" => [
|
||||
"sFileToFind" => $sFileToFind3,
|
||||
"aAllImports" => [$sFileToFind4, $sFileToFind5]
|
||||
],
|
||||
"scss with simple @imports in another folder" => [
|
||||
"sFileToFind" => "css/simple_import2.scss",
|
||||
"aAllImports" => [$sFileToFind5]
|
||||
],
|
||||
"scss with @imports shortcut included_file3 => _included_file3.scss" => [
|
||||
"sFileToFind" => "css/shortcut.scss",
|
||||
"aAllImports" => ["css/_included_file3.scss", "css/included_scss/included_file4.scss"]
|
||||
],
|
||||
"scss with @imports shortcut same file and folder names => feature1/_feature1.scss" => [
|
||||
"sFileToFind" => "css/shortcut2.scss",
|
||||
"aAllImports" => ["css/feature1/_feature1.scss"],
|
||||
],
|
||||
"cross_reference & infinite loop" => [
|
||||
"sFileToFind" => "css/cross_reference1.scss",
|
||||
"aAllImports" => ["css/cross_reference2.scss"],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPath
|
||||
*
|
||||
* @return string replace '/' by appropriate dir separator, depending on OS
|
||||
*
|
||||
* @uses DIRECTORY_SEPARATOR
|
||||
*/
|
||||
private function UpdateDirSep(string $sPath)
|
||||
{
|
||||
return str_replace('/', DIRECTORY_SEPARATOR, $sPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sPath
|
||||
* @param $sExpectedCanonicalPath
|
||||
*
|
||||
* @dataProvider CanonicalizePathProvider
|
||||
*/
|
||||
public function testCanonicalizePath($sExpectedCanonicalPath, $sPath)
|
||||
{
|
||||
$this->assertEquals($sExpectedCanonicalPath, ThemeHandler::CanonicalizePath($sPath), "Failed to reduce path $sPath");
|
||||
}
|
||||
|
||||
public function CanonicalizePathProvider()
|
||||
{
|
||||
return [
|
||||
[ '/var/www/html/iTop/images/itop-logo-2.png', '/var/www/html/iTop/env-production/branding/themes/light-grey/../../../../images/itop-logo-2.png' ],
|
||||
[ '/var/www/html/iTop/env-production/branding/themes/light-grey/images/', '/var/www/html/iTop/env-production/branding/themes/light-grey/images/' ],
|
||||
[ '/var/www/html/iTop/css/ui-lightness/images/ui-icons_222222_256x240.png', '/var/www/html/iTop/env-production//branding/themes/light-grey//../../../../css/ui-lightness/images/ui-icons_222222_256x240.png' ]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace UI\Base\Layout;
|
||||
|
||||
use ApplicationContext;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenu;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
* Class NavigationMenuTest
|
||||
*
|
||||
* @package UI\Base\Layout
|
||||
*/
|
||||
class NavigationMenuTest extends ItopDataTestCase {
|
||||
public function IsAllowedProvider(){
|
||||
return [
|
||||
'show menu' => [ true ],
|
||||
'hide menu' => [ false ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider IsAllowedProvider
|
||||
* test used to make sure backward compatibility is ensured
|
||||
*/
|
||||
public function testIsAllowed($bExpectedIsAllowed=true){
|
||||
\MetaModel::GetConfig()->Set('navigation_menu.show_organization_filter', $bExpectedIsAllowed);
|
||||
$oNavigationMenu = new NavigationMenu(
|
||||
$this->createMock(ApplicationContext::class),
|
||||
$this->createMock(PopoverMenu::class));
|
||||
|
||||
$isAllowed = $oNavigationMenu->IsSiloSelectionEnabled();
|
||||
$this->assertEquals($bExpectedIsAllowed, $isAllowed);
|
||||
}
|
||||
|
||||
public function testIsAllowed_BackwardCompatibility_NoVariableInConfFile(){
|
||||
\MetaModel::GetConfig()->Set('navigation_menu.show_organization_filter', false);
|
||||
|
||||
$sTmpFilePath = tempnam(sys_get_temp_dir(), 'test_');
|
||||
$oInitConfig = \MetaModel::GetConfig();
|
||||
$oInitConfig->WriteToFile($sTmpFilePath);
|
||||
|
||||
//remove variable for the test
|
||||
$aLines = file($sTmpFilePath);
|
||||
|
||||
$aRows = array();
|
||||
|
||||
foreach ($aLines as $key => $sLine) {
|
||||
if (!preg_match('/navigation_menu.show_organization_filter/', $sLine)) {
|
||||
$aRows[] = $sLine;
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($sTmpFilePath, implode("\n", $aRows));
|
||||
$oTempConfig = new \Config($sTmpFilePath);
|
||||
|
||||
$isAllowed = $oTempConfig->Get('navigation_menu.show_organization_filter');
|
||||
|
||||
$this->assertEquals(true, $isAllowed);
|
||||
unlink($sTmpFilePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?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\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
use privUITransactionFile;
|
||||
use UserRights;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*
|
||||
* @covers utils
|
||||
* @group sampleDataNeeded
|
||||
* @group defaultProfiles
|
||||
*/
|
||||
class privUITransactionFileTest extends ItopDataTestCase
|
||||
{
|
||||
/** @var int ID of the "support agent" pofile in the sample data */
|
||||
const SAMPLE_DATA_SUPPORT_PROFILE_ID = 5;
|
||||
const USER1_TEST_LOGIN = 'user1_support_test_privUITransaction';
|
||||
const USER2_TEST_LOGIN = 'user2_support_test_privUITransaction';
|
||||
|
||||
/**
|
||||
* @dataProvider cleanupOldTransactionsProvider
|
||||
*/
|
||||
public function testCleanupOldTransactions($iCleanableCreated, $iPreservableCreated, $sCleanablePrefix, $sPreservablePrefix)
|
||||
{
|
||||
MetaModel::GetConfig()->Set('transactions_gc_threshold', 100);
|
||||
|
||||
$iBaseLimit = time() - 24*3600; //24h
|
||||
|
||||
$sBaseDir = sys_get_temp_dir();
|
||||
$sDir = "$sBaseDir/privUITransactionFileTest/cleanupOldTransactions";
|
||||
if (is_dir($sDir)) {
|
||||
$this->rm($sDir);
|
||||
}
|
||||
mkdir("$sDir", 0777, true);
|
||||
|
||||
for ($i = 0; $i < $iCleanableCreated; $i++) {
|
||||
touch("$sDir/{$sCleanablePrefix}$i", $iBaseLimit - 10*60);
|
||||
}
|
||||
for ($i = 0; $i < $iPreservableCreated; $i++) {
|
||||
touch("$sDir/{$sPreservablePrefix}$i", $iBaseLimit + 10*60);
|
||||
}
|
||||
|
||||
$iCleanableCount = count(glob("$sDir/{$sCleanablePrefix}*"));
|
||||
$iPreservableCount = count(glob("$sDir/{$sPreservablePrefix}*"));
|
||||
$this->assertEquals($iCleanableCreated, $iCleanableCount);
|
||||
$this->assertEquals($iPreservableCreated, $iPreservableCount);
|
||||
|
||||
$aArgs = [
|
||||
'sTransactionDir' => "$sDir",
|
||||
];
|
||||
$oprivUITransactionFile = new privUITransactionFile();
|
||||
$this->InvokeNonPublicMethod(get_class($oprivUITransactionFile), 'CleanupOldTransactions', $oprivUITransactionFile, $aArgs);
|
||||
|
||||
$iCleanableCount = count(glob("$sDir/{$sCleanablePrefix}*"));
|
||||
$iPreservableCount = count(glob("$sDir/{$sPreservablePrefix}*"));
|
||||
$this->assertEquals(0, $iCleanableCount);
|
||||
$this->assertEquals($iPreservableCreated, $iPreservableCount);
|
||||
}
|
||||
|
||||
public function cleanupOldTransactionsProvider()
|
||||
{
|
||||
$iBaseLimit = time() - 60 * 10; //ten minutes ago
|
||||
|
||||
$sBaseDir = sys_get_temp_dir();
|
||||
$sDir = "$sBaseDir/privUITransactionFileTest/cleanupOldTransactions";
|
||||
|
||||
return [
|
||||
'linux - no content' => [
|
||||
'iCleanableCreated' => 0,
|
||||
'iPreservableCreated' => 0,
|
||||
'sCleanablePrefix' => 'cleanable-',
|
||||
'sPreservablePrefix' => 'preservable-',
|
||||
],
|
||||
'linux - cleanable content' => [
|
||||
'iCleanableCreated' => 2,
|
||||
'iPreservableCreated' => 0,
|
||||
'sCleanablePrefix' => 'cleanable-',
|
||||
'sPreservablePrefix' => 'preservable-',
|
||||
],
|
||||
'linux - preseved content' => [
|
||||
'iCleanableCreated' => 0,
|
||||
'iPreservableCreated' => 2,
|
||||
'sCleanablePrefix' => 'cleanable-',
|
||||
'sPreservablePrefix' => 'preservable-',
|
||||
],
|
||||
'win - no content' => [
|
||||
'iCleanableCreated' => 0,
|
||||
'iPreservableCreated' => 0,
|
||||
'sCleanablePrefix' => 'cle',
|
||||
'sPreservablePrefix' => 'pre',
|
||||
],
|
||||
'win - cleanable content' => [
|
||||
'iCleanableCreated' => 2,
|
||||
'iPreservableCreated' => 0,
|
||||
'sCleanablePrefix' => 'cle',
|
||||
'sPreservablePrefix' => 'pre',
|
||||
],
|
||||
'win - preseved content' => [
|
||||
'iCleanableCreated' => 0,
|
||||
'iPreservableCreated' => 2,
|
||||
'sCleanablePrefix' => 'cle',
|
||||
'sPreservablePrefix' => 'pre',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function rm($sDir) {
|
||||
$aFiles = array_diff(scandir($sDir), ['.','..']);
|
||||
foreach ($aFiles as $sFile) {
|
||||
if ((is_dir("$sDir/$sFile"))) {
|
||||
$this->rm("$sDir/$sFile");
|
||||
} else {
|
||||
unlink("$sDir/$sFile");
|
||||
}
|
||||
}
|
||||
return rmdir($sDir);
|
||||
}
|
||||
|
||||
const USER_TEST_LOGIN = 'support_test_privUITransaction';
|
||||
|
||||
/**
|
||||
* @throws \SecurityException
|
||||
* @uses self::SAMPLE_DATA_SUPPORT_PROFILE_ID
|
||||
* @uses self::USER1_TEST_LOGIN
|
||||
* @uses self::USER2_TEST_LOGIN
|
||||
*/
|
||||
public function testIsTransactionValid()
|
||||
{
|
||||
$this->CreateUser(static::USER1_TEST_LOGIN, self::SAMPLE_DATA_SUPPORT_PROFILE_ID);
|
||||
$this->CreateUser(static::USER2_TEST_LOGIN, self::SAMPLE_DATA_SUPPORT_PROFILE_ID);
|
||||
|
||||
// created users aren't admin, so each one can't see the other (\UserRights::GetSelectFilter)
|
||||
// If calling \UserRights::Login(user1) then \UserRights::Login(user2) we won't be able to load user2 !
|
||||
// As now we are in the admin context, we are calling FindUser() directly so that user objects will be saved in the UserRights cache !
|
||||
// we can skip doing this for user1 as the first \UserRights::Login call will initialize the UserRights cache !
|
||||
$this->InvokeNonPublicStaticMethod(UserRights::class, 'FindUser', [self::USER2_TEST_LOGIN]);
|
||||
|
||||
// create token in the user1 context
|
||||
$bUser1Login1 = UserRights::Login(self::USER1_TEST_LOGIN);
|
||||
$this->assertTrue($bUser1Login1, 'Login with user1 throw an error');
|
||||
$sTransactionIdUserSupport = privUITransactionFile::GetNewTransactionId();
|
||||
$bResult = privUITransactionFile::IsTransactionValid($sTransactionIdUserSupport, false);
|
||||
$this->assertTrue($bResult, 'Token created by support user must be valid in the support user context');
|
||||
|
||||
// test token in the user2 context
|
||||
$bUser2Login = UserRights::Login(self::USER2_TEST_LOGIN);
|
||||
$this->assertTrue($bUser2Login, 'Login with user2 throw an error');
|
||||
$bResult = privUITransactionFile::IsTransactionValid($sTransactionIdUserSupport, false);
|
||||
$this->assertFalse($bResult, 'Token created by support user must be invalid in the admin user context');
|
||||
$bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUserSupport);
|
||||
$this->assertFalse($bResult, 'Token created by support user cannot be removed in the admin user context');
|
||||
|
||||
// test other methods in the user1 context
|
||||
$bUser1Login2 = UserRights::Login(self::USER1_TEST_LOGIN);
|
||||
$this->assertTrue($bUser1Login2, 'Login with user1 throw an error');
|
||||
$bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUserSupport);
|
||||
$this->assertTrue($bResult, 'Token created by support user must be removed in the support user context');
|
||||
|
||||
// test when no user logged (combodo-unauthenticated-form module for example)
|
||||
UserRights::_ResetSessionCache();
|
||||
$sTransactionIdUnauthenticatedUser = privUITransactionFile::GetNewTransactionId();
|
||||
$bResult = privUITransactionFile::IsTransactionValid($sTransactionIdUnauthenticatedUser, false);
|
||||
$this->assertTrue($bResult, 'Token created by unauthenticated user must be valid when no user logged');
|
||||
$bResult = privUITransactionFile::RemoveTransaction($sTransactionIdUnauthenticatedUser);
|
||||
$this->assertTrue($bResult, 'Token created by unauthenticated user must be removed when no user logged');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2018 Dennis Lassiter
|
||||
*
|
||||
* 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\Application\Search;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use MetaModel;
|
||||
use Query;
|
||||
use QueryOQL;
|
||||
|
||||
/**
|
||||
* This test creates call export on requests and check request usage counter.
|
||||
*
|
||||
* Transaction are disabled to avoid data inconsistency between test and call to export (outside test scope)
|
||||
* All objects created in this test will be deleted by the test.
|
||||
*
|
||||
* @group iTopQuery
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class QueryTest extends ItopDataTestCase
|
||||
{
|
||||
// disable transaction to avoid data inconsistency between test and call to export (outside test scope)
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
// user for exportation process
|
||||
const USER = 'dani2';
|
||||
const PASSWORD = '1TopCombodo+';
|
||||
private $oUser;
|
||||
|
||||
/** @inheritDoc */
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// create export user
|
||||
$this->CreateExportUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new user for export authentication purpose.
|
||||
*/
|
||||
private function CreateExportUser()
|
||||
{
|
||||
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => 'Administrator'), true);
|
||||
$this->oUser = $this->CreateUser(self::USER, $oAdminProfile->GetKey(), self::PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OQL query to list Person objects.
|
||||
*
|
||||
* @param string $sName query name
|
||||
* @param string $sDescription query description
|
||||
* @param string $sOql query oql phrase
|
||||
* @param string|null $sFields fields to export
|
||||
*/
|
||||
private function CreateQueryOQL(string $sName, string $sDescription, string $sOql, string $sFields = null) : QueryOQL
|
||||
{
|
||||
$oQuery = new QueryOQL();
|
||||
$oQuery->Set('name', $sName);
|
||||
$oQuery->Set('description', $sDescription);
|
||||
$oQuery->Set('oql', $sOql);
|
||||
|
||||
if($sFields != null){
|
||||
$oQuery->Set('fields', $sFields);
|
||||
}
|
||||
|
||||
$oQuery->DBInsert();
|
||||
|
||||
return $oQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test query export V1 usage.
|
||||
*
|
||||
* @param string $sDescription query description
|
||||
* @param string $sOql query oql phrase
|
||||
*
|
||||
* @dataProvider getQueryProvider
|
||||
*/
|
||||
public function testQueryExportV1Usage(string $sDescription, string $sOql)
|
||||
{
|
||||
// create query OQL
|
||||
$oQuery = $this->CreateQueryOQL($this->dataName(), $sDescription, $sOql);
|
||||
|
||||
// call export service
|
||||
$this->CallExportService($oQuery);
|
||||
|
||||
// reload to update counter (done by export process)
|
||||
$oQuery->Reload();
|
||||
|
||||
// extract counter
|
||||
$iResult = $oQuery->Get('export_count');
|
||||
|
||||
// delete the query
|
||||
$oQuery->DBDelete();
|
||||
|
||||
// test
|
||||
$this->assertEquals(1, $iResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test query export V2 usage.
|
||||
*
|
||||
* @param string $sDescription query description
|
||||
* @param string $sOql query oql phrase
|
||||
*
|
||||
* @dataProvider getQueryProvider
|
||||
*/
|
||||
public function testQueryExportV2Usage(string $sDescription, string $sOql)
|
||||
{
|
||||
// create query OQL
|
||||
$oQuery = $this->CreateQueryOQL($this->dataName(), $sDescription, $sOql, 'first_name');
|
||||
|
||||
// call export service
|
||||
$this->CallExportService($oQuery);
|
||||
|
||||
// reload to update counter (done by export process)
|
||||
$oQuery->Reload();
|
||||
|
||||
// extract counter
|
||||
$iResult = $oQuery->Get('export_count');
|
||||
|
||||
// delete the query
|
||||
$oQuery->DBDelete();
|
||||
|
||||
// test
|
||||
$this->assertEquals(1, $iResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provide for test.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getQueryProvider()
|
||||
{
|
||||
return array(
|
||||
'Export #1' => array('query without params', 'SELECT Person'),
|
||||
'Export #2' => array('query with params', "SELECT Person WHERE first_name LIKE 'B%'")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call export for given query object.
|
||||
*
|
||||
* @param \Query $oQuery
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
private function CallExportService(Query $oQuery)
|
||||
{
|
||||
// compute request url
|
||||
$url = $oQuery->GetExportUrl();
|
||||
|
||||
// open curl
|
||||
$curl = curl_init();
|
||||
|
||||
// curl options
|
||||
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($curl, CURLOPT_USERPWD, self::USER . ':' . self::PASSWORD);
|
||||
curl_setopt($curl, CURLOPT_URL, $url);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
// execute curl
|
||||
$result = curl_exec($curl);
|
||||
|
||||
// close curl
|
||||
curl_close($curl);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->oUser->DBDelete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*!
|
||||
* Copyright (C) 2013-2020 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
|
||||
*/
|
||||
$approot-relative: "../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
// Base colors
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$gray-dark: #444 !default;
|
||||
$gray: #777 !default;
|
||||
$gray-light: #808080 !default;
|
||||
$gray-lighter: #ddd !default;
|
||||
$gray-extra-light: #F1F1F1 !default;
|
||||
|
||||
$white: #FFFFFF !default;
|
||||
|
||||
$combodo-orange: #EA7D1E !default;
|
||||
$combodo-dark-gray: #585653 !default;
|
||||
|
||||
$combodo-orange-dark: darken($combodo-orange, 13.8%) !default;
|
||||
$combodo-orange-darker: darken($combodo-orange, 18%) !default;
|
||||
$combodo-dark-gray-dark: darken($combodo-dark-gray, 13.5%) !default;
|
||||
$combodo-dark-gray-darker: darken($combodo-dark-gray, 18%) !default;
|
||||
|
||||
// Brand colors
|
||||
// - Bases
|
||||
$brand-primary: $combodo-orange !default;
|
||||
$brand-secondary: $combodo-dark-gray !default;
|
||||
// - Shades
|
||||
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
|
||||
$brand-primary-lighter: lighten($brand-primary, 10%) !default;
|
||||
$brand-primary-light: lighten($brand-primary, 6%) !default;
|
||||
$brand-primary-dark: darken($brand-primary, 6%) !default;
|
||||
$brand-primary-darker: darken($brand-primary, 10%) !default;
|
||||
$brand-primary-darkest: darken($brand-primary, 15%) !default;
|
||||
$brand-secondary-lightest: lighten($brand-secondary, 15%) !default;
|
||||
$brand-secondary-lighter: lighten($brand-secondary, 10%) !default;
|
||||
$brand-secondary-light: lighten($brand-secondary, 6%) !default;
|
||||
$brand-secondary-dark: darken($brand-secondary, 6%) !default;
|
||||
$brand-secondary-darker: darken($brand-secondary, 10%) !default;
|
||||
$brand-secondary-darkest: darken($brand-secondary, 15%) !default;
|
||||
|
||||
// Vars
|
||||
$highlight-color: $brand-primary !default;
|
||||
$grey-color: #555555 !default;
|
||||
$complement-color: #1c94c4 !default;
|
||||
$complement-light: #d6e8ef !default;
|
||||
$frame-background-color: $gray-extra-light !default;
|
||||
$text-color: #000 !default;
|
||||
$box-radius: 0px !default;
|
||||
$box-shadow-regular: 0 1px 1px rgba(0, 0, 0, 0.15) !default;
|
||||
$body-background-color : $white !default;
|
||||
|
||||
$hyperlink-color: $complement-color !default;
|
||||
$hyperlink-text-decoration: none !default;
|
||||
|
||||
////////////
|
||||
// Search //
|
||||
$search-form-container-color: $white !default;
|
||||
$search-form-container-bg-color: $complement-color !default;
|
||||
//
|
||||
$search-criteria-box-color: #2D2D2D !default;
|
||||
$search-criteria-box-picto-color: $brand-primary !default;
|
||||
$search-criteria-box-bg-color: #EEEEEE !default;
|
||||
$search-criteria-box-hover-color: $white !default;
|
||||
$search-criteria-box-border-color: #CCCCCC !default;
|
||||
$search-criteria-box-border: 1px solid $search-criteria-box-border-color !default;
|
||||
$search-criteria-box-radius: 1px !default;
|
||||
$search-criteria-more-less-details-color: #3F7294 !default;
|
||||
//
|
||||
$search-add-criteria-box-color: $search-criteria-box-color !default;
|
||||
$search-add-criteria-box-bg-color: $white !default;
|
||||
$search-add-criteria-box-hover-color: $gray-extra-light !default;
|
||||
//
|
||||
$search-button-box-color: $brand-primary !default;
|
||||
$search-button-box-bg-color: $white !default;
|
||||
$search-button-box-bg-hover-color: $gray-extra-light !default;
|
||||
|
||||
/////////////
|
||||
// Buttons //
|
||||
/////////////
|
||||
// Toggle button
|
||||
$toggle-button-bg-color: #CCCCCC !default;
|
||||
$toggle-button-bg-checked-color: $brand-primary !default;
|
||||
$toggle-button-slider-bg-color: #FFFFFF !default;
|
||||
|
||||
// Console elements
|
||||
$summary-details-background: $grey-color !default;
|
||||
$main-header-background: $frame-background-color !default;
|
||||
$table-even-background: $frame-background-color !default;
|
||||
$table-hover-background: #fdf5d0 !default;
|
||||
$popup-menu-highlight-color: $highlight-color !default;
|
||||
$popup-menu-text-color: #000 !default;
|
||||
$popup-menu-background-color: #fff !default;
|
||||
$popup-menu-text-higlight-color: #fff !default;
|
||||
$breadcrumb-color: $grey-color !default;
|
||||
$breadcrumb-highlight-color: $highlight-color !default;
|
||||
|
||||
// jQuery UI widgets vars
|
||||
$primary-text-color: #333333 !default;
|
||||
$secondary-text-color: $grey-color !default;
|
||||
$error-text-color: $white !default;
|
||||
$highlight-text-color: #363636 !default;
|
||||
$hover-background-color: #fde17c !default;
|
||||
$border-highlight-color: $brand-primary-dark !default;
|
||||
$highlight-item-color: $white !default;
|
||||
$content-color: #eeeeee !default;
|
||||
$default-font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif !default;
|
||||
$icons-filter: hue-rotate(0deg) !default;
|
||||
@@ -0,0 +1,2 @@
|
||||
Files in this folder are not meant to be updated along with their original copies in /css.
|
||||
Check with Olivier for more information on their purpose / test case.
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
@import 'cross_reference2.scss';
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
@import 'cross_reference1.scss';
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
@import 'included_file1.scss';
|
||||
@import 'included_scss/included_file2.scss';
|
||||
|
||||
/*!
|
||||
* Combodo portal template v1.0.0
|
||||
*/
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
@import 'included_file3';
|
||||
@import 'included_scss/included_file4';
|
||||
@@ -0,0 +1 @@
|
||||
@import 'feature1/feature1';
|
||||
2
tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/simple_import.scss
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
@import 'included_file1.scss';
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
@import 'included_scss/included_file2.scss';
|
||||
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 312 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 61 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 64 B |
|
After Width: | Height: | Size: 324 B |
|
After Width: | Height: | Size: 676 B |
|
After Width: | Height: | Size: 842 B |
|
After Width: | Height: | Size: 54 B |
|
After Width: | Height: | Size: 543 B |
|
After Width: | Height: | Size: 274 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 327 B |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 56 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 850 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 643 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 122 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 85 B |
|
After Width: | Height: | Size: 781 B |
|
After Width: | Height: | Size: 89 B |
|
After Width: | Height: | Size: 787 B |
|
After Width: | Height: | Size: 65 B |
|
After Width: | Height: | Size: 750 B |
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
=== SIGNATURE BEGIN ===
|
||||
{"variables":"37c31105548fce44fecca5cb34e455c9","stylesheets":{"jqueryui":"78cfafc3524dac98e61fc2460918d4e5","main":"52d8a7c5530ceb3a4d777364fa4e1eea"},"variable_imports":{"css-variables":"3c3f5adf98b9dbf893658314436c4b93"},"images":{"css\/ui-lightness\/images\/ui-icons_222222_256x240.png":"3a3c5468f484f07ac4a320d9e22acb8c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_20_666666_40x40.png":"4429d568c67d8dfeb9040273ea0fb8c4","css\/ui-lightness\/images\/ui-icons_E87C1E_256x240.png":"7003dd36cb2aa032c8ec871ce4d4e03d","css\/ui-lightness\/images\/ui-icons_1c94c4_256x240.png":"dbd693dc8e0ef04e90a2f7ac7b390086","css\/ui-lightness\/images\/ui-icons_F26522_256x240.png":"16278ec0c07270be571f4c2e97fcc10c","css\/ui-lightness\/images\/ui-bg_diagonals-thick_18_b81900_40x40.png":"e460a66d4b3e093fc651e62a236267cb","css\/ui-lightness\/images\/ui-icons_ffffff_256x240.png":"41612b0f4a034424f8321c9f824a94da","css\/ui-lightness\/images\/ui-icons_ffd27a_256x240.png":"dda1b6f694b0d196aefc66a1d6d758f6","images\/actions_right.png":"31c8906bd25d27b83a0a2466bf903462","images\/ac-background.gif":"76135f3697b41a15aed787cfd77776c7","images\/green-square.gif":"16ea9a497d72f5e66e4e8ea9ae08024e","images\/tv-item.gif":"719fe2d4566108e73162fb8868d3778c","images\/tv-collapsable.gif":"63a3351ea0d580797c9b8c386aa4f48b","images\/tv-expandable.gif":"a2d1af4128e4a798a7f3390b12a28574","images\/tv-item-last.gif":"2ae7e1d9972ce71e5caa65a086bc5b7e","images\/tv-collapsable-last.gif":"71acaa9d7c2616e9e8b7131a75ca65da","images\/tv-expandable-last.gif":"9d51036b3a8102742709da66789fd0f7","images\/red-header.gif":"c73b8765f0c8c3c183cb6a0c2bb0ec69","images\/green-header.gif":"0e22a09bb8051b2a274b3427ede62e82","images\/orange-header.gif":"ce1f93f0af64431771b4cbd6c99c567b","images\/calendar.png":"ab56e59af3c96ca661821257d376465e","images\/truncated.png":"c6f91108afe8159d417b4dc556cd3b2a","images\/plus.gif":"f00e1e6e1161f48608bb2bbc79b9948c","images\/minus.gif":"6d77c0c0c2f86b6995d1cdf78274eaab","images\/full-screen.png":"b541fadd3f1563856a4b44aeebd9d563","images\/indicator.gif":"03ce3dcc84af110e9da8699a841e5200","images\/delete.png":"93c047549c31a270a037840277cf59d3","images\/info-mini.png":"445c090ed777c5e6a08ac390fa896193","images\/ok.png":"f6973773335fd83d8d2875f9a3c925af","images\/error.png":"1af8a1041016f67669c5fd22dc88c82e","images\/eye-open-555.png":"9940f4e5b1248042c238e1924359fd5e","images\/eye-closed-555.png":"6ad3b0bae791bf61addc9d8ca80a642d","images\/eye-open-fff.png":"b7db2402d4d5c72314c25790a66150d4","images\/eye-closed-fff.png":"f9be7454dbb47b0e0bca3aa370ae7db5"},"utility_imports":[]}
|
||||
=== SIGNATURE END ===
|
||||
*/
|
||||
====CSSCOMPILEDCONTENT====
|
||||
@@ -0,0 +1 @@
|
||||
{"variables":{"brand-primary":"#C53030","hover-background-color":"#F6F6F6","icons-filter":"grayscale(1)","search-form-container-bg-color":"#4A5568"},"utility_imports":[],"variable_imports":{"css-variables":"..\/css\/DO_NOT_CHANGE.css-variables.scss"},"stylesheets":{"jqueryui":"..\/css\/ui-lightness\/DO_NOT_CHANGE.jqueryui.scss","main":"..\/css\/DO_NOT_CHANGE.light-grey.scss"}}
|
||||
@@ -0,0 +1,36 @@
|
||||
[
|
||||
"css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png",
|
||||
"css/ui-lightness/images/ui-icons_ffffff_256x240.png",
|
||||
"images/actions_right.png",
|
||||
"images/ac-background.gif",
|
||||
"images/green-square.gif",
|
||||
"images/tv-item.gif",
|
||||
"images/tv-collapsable.gif",
|
||||
"images/tv-expandable.gif",
|
||||
"images/tv-item-last.gif",
|
||||
"images/tv-collapsable-last.gif",
|
||||
"images/tv-expandable-last.gif",
|
||||
"images/red-header.gif",
|
||||
"images/green-header.gif",
|
||||
"images/orange-header.gif",
|
||||
"images/calendar.png",
|
||||
"images/truncated.png",
|
||||
"images/plus.gif",
|
||||
"images/minus.gif",
|
||||
"images/full-screen.png",
|
||||
"images/indicator.gif",
|
||||
"images/delete.png",
|
||||
"images/info-mini.png",
|
||||
"images/ok.png",
|
||||
"images/error.png",
|
||||
"images/eye-open-555.png",
|
||||
"images/eye-closed-555.png",
|
||||
"images/eye-open-fff.png",
|
||||
"images/eye-closed-fff.png",
|
||||
"css/ui-lightness/images/ui-icons_222222_256x240.png",
|
||||
"css/ui-lightness/images/ui-icons_E87C1E_256x240.png",
|
||||
"css/ui-lightness/images/ui-icons_1c94c4_256x240.png",
|
||||
"css/ui-lightness/images/ui-icons_F26522_256x240.png",
|
||||
"css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png",
|
||||
"css/ui-lightness/images/ui-icons_ffd27a_256x240.png"
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
$approot-relative: "../../../../../"
|
||||
$version:"aaa"
|
||||
background-image: url('abc/'+ $approot-relative + "css/ui-lightness/images/toutou.png?v=" + $version);
|
||||
background-image: url($approot-relative + "css/ui-lightness/images/toto.png?v=" + $version);
|
||||
background: #666666 url($approot-relative + 'css/ui-lightness/images/titi.gif?v=' + $version1) 50% 50% repeat;
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7?v=" + $version);
|
||||
background-image: url('css/ui-lightness/images/tutu.jpg');
|
||||
background-image: url("css/ui-lightness/images/tata.jpeg");
|
||||
background-image: url("css/ui-lightness/images/tete.jpeg?g=" + $attr);
|
||||
url($approot-relative + 'node_modules/raleway-webfont/fonts/Raleway-Thin.jpeg') format('truetype'),
|
||||
808
tests/php-unit-tests/unitary-tests/application/utilsTest.php
Normal file
@@ -0,0 +1,808 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2018 Dennis Lassiter
|
||||
*
|
||||
* 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\Application;
|
||||
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* @covers utils
|
||||
*/
|
||||
class utilsTest extends ItopTestCase
|
||||
{
|
||||
public function testEndsWith()
|
||||
{
|
||||
$this->assertFalse(utils::EndsWith('a', 'bbbb'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider memoryLimitDataProvider
|
||||
*/
|
||||
public function testIsMemoryLimit($expected, $memoryLimit, $requiredMemory)
|
||||
{
|
||||
$this->assertSame($expected, utils::IsMemoryLimitOk($memoryLimit, $requiredMemory));
|
||||
}
|
||||
|
||||
/**
|
||||
* DataProvider for testIsMemoryLimitOk
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function memoryLimitDataProvider()
|
||||
{
|
||||
return [
|
||||
'current -1, required 1024' => [true, -1, 1024],
|
||||
'current 1024, required 1024' => [true, 1024, 1024],
|
||||
'current 2048, required 1024' => [true, 2048, 1024],
|
||||
'current 1024, required 2048' => [false, 1024, 2048],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider realPathDataProvider
|
||||
* @covers utils::RealPath()
|
||||
*/
|
||||
public function testRealPath($sPath, $sBasePath, $expected)
|
||||
{
|
||||
$this->assertSame($expected, utils::RealPath($sPath, $sBasePath), "utils::RealPath($sPath, $sBasePath) does not match $expected");
|
||||
}
|
||||
|
||||
public function realPathDataProvider()
|
||||
{
|
||||
parent::setUp(); // if not called, APPROOT won't be defined :(
|
||||
|
||||
$sSep = DIRECTORY_SEPARATOR;
|
||||
$sItopRootRealPath = realpath(APPROOT).$sSep;
|
||||
$sLicenseFileName = 'license.txt';
|
||||
if (!is_file(APPROOT.$sLicenseFileName))
|
||||
{
|
||||
$sLicenseFileName = 'LICENSE';
|
||||
}
|
||||
|
||||
return [
|
||||
$sLicenseFileName => [APPROOT.$sLicenseFileName, APPROOT, $sItopRootRealPath.$sLicenseFileName],
|
||||
'unexisting file' => [APPROOT.'license_DOES_NOT_EXIST.txt', APPROOT, false],
|
||||
'/'.$sLicenseFileName => [APPROOT.$sSep.$sLicenseFileName, APPROOT, $sItopRootRealPath.$sLicenseFileName],
|
||||
'%2f'.$sLicenseFileName => [APPROOT.'%2f'. $sLicenseFileName, APPROOT, false],
|
||||
'../'.$sLicenseFileName => [APPROOT.'..'.$sSep.$sLicenseFileName, APPROOT, false],
|
||||
'%2e%2e%2f'.$sLicenseFileName => [APPROOT.'%2e%2e%2f'.$sLicenseFileName, APPROOT, false],
|
||||
'application/utils.inc.php with basepath=APPROOT' => [
|
||||
APPROOT.'application/utils.inc.php',
|
||||
APPROOT,
|
||||
$sItopRootRealPath.'application'.$sSep.'utils.inc.php',
|
||||
],
|
||||
'application/utils.inc.php with basepath=APPROOT/application' => [
|
||||
APPROOT.'application/utils.inc.php',
|
||||
APPROOT.'application',
|
||||
$sItopRootRealPath.'application'.$sSep.'utils.inc.php',
|
||||
],
|
||||
'basepath containing / and \\' => [
|
||||
APPROOT.'sources/Form/Form.php',
|
||||
APPROOT.'sources/Form\\Form.php',
|
||||
$sItopRootRealPath.'sources'.$sSep.'Form'.$sSep.'Form.php',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider LocalPathProvider
|
||||
*
|
||||
* @param $sAbsolutePath
|
||||
* @param $expected
|
||||
*/
|
||||
public function testLocalPath($sAbsolutePath, $expected)
|
||||
{
|
||||
$this->assertSame($expected, utils::LocalPath($sAbsolutePath));
|
||||
|
||||
}
|
||||
|
||||
public function LocalPathProvider()
|
||||
{
|
||||
return array(
|
||||
'index.php' => array(
|
||||
'sAbsolutePath' => APPROOT.'index.php',
|
||||
'expected' => 'index.php',
|
||||
),
|
||||
'non existing' => array(
|
||||
'sAbsolutePath' => APPROOT.'nonexisting/nonexisting',
|
||||
'expected' => false,
|
||||
),
|
||||
'outside' => array(
|
||||
'sAbsolutePath' => '/tmp',
|
||||
'expected' => false,
|
||||
),
|
||||
'application/cmdbabstract.class.inc.php' => array(
|
||||
'sAbsolutePath' => APPROOT.'application/cmdbabstract.class.inc.php',
|
||||
'expected' => 'application/cmdbabstract.class.inc.php',
|
||||
),
|
||||
'dir' => array(
|
||||
'sAbsolutePath' => APPROOT.'application/.',
|
||||
'expected' => 'application',
|
||||
),
|
||||
'root' => array(
|
||||
'sAbsolutePath' => APPROOT.'.',
|
||||
'expected' => '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider appRootUrlProvider
|
||||
* @covers utils::GetAppRootUrl
|
||||
*/
|
||||
public function testGetAppRootUrl($sReturnValue, $sCurrentScript, $sAppRoot, $sAbsoluteUrl)
|
||||
{
|
||||
$this->assertEquals($sReturnValue, utils::GetAppRootUrl($sCurrentScript, $sAppRoot, $sAbsoluteUrl));
|
||||
}
|
||||
|
||||
public function appRootUrlProvider()
|
||||
{
|
||||
return array(
|
||||
'Setup index (windows antislash)' => array('http://localhost/', 'C:\Dev\wamp64\www\itop-dev\setup\index.php', 'C:\Dev\wamp64\www\itop-dev', 'http://localhost/setup/'),
|
||||
'Setup index (windows slash)' => array('http://127.0.0.1/', 'C:/web/setup/index.php', 'C:/web', 'http://127.0.0.1/setup/'),
|
||||
'Setup index (windows slash, drive letter case difference)' => array('http://127.0.0.1/', 'c:/web/setup/index.php', 'C:/web', 'http://127.0.0.1/setup/'),
|
||||
);
|
||||
}
|
||||
|
||||
public function GetAbsoluteUrlAppRootPersistency() {
|
||||
$this->setUp();
|
||||
|
||||
return [
|
||||
'ForceTrustProxy 111' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => true,
|
||||
'sExpectedAppRootUrl1' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy2' => true,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => true,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 101' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => true,
|
||||
'sExpectedAppRootUrl1' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy2' => false,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => true,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 011' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => false,
|
||||
'sExpectedAppRootUrl1' => 'http://example.com/',
|
||||
'bForceTrustProxy2' => true,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => true,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 110' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => true,
|
||||
'sExpectedAppRootUrl1' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy2' => true,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => false,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 010' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => false,
|
||||
'sExpectedAppRootUrl1' => 'http://example.com/',
|
||||
'bForceTrustProxy2' => true,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => false,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 001' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => false,
|
||||
'sExpectedAppRootUrl1' => 'http://example.com/',
|
||||
'bForceTrustProxy2' => false,
|
||||
'sExpectedAppRootUrl2' => 'http://example.com/',
|
||||
'bForceTrustProxy3' => true,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'ForceTrustProxy 000' => [
|
||||
'bBehindReverseProxy' => false,
|
||||
'bForceTrustProxy1' => false,
|
||||
'sExpectedAppRootUrl1' => 'http://example.com/',
|
||||
'bForceTrustProxy2' => false,
|
||||
'sExpectedAppRootUrl2' => 'http://example.com/',
|
||||
'bForceTrustProxy3' => false,
|
||||
'sExpectedAppRootUrl3' => 'http://example.com/',
|
||||
],
|
||||
'BehindReverseProxy ForceTrustProxy 010' => [
|
||||
'bBehindReverseProxy' => true,
|
||||
'bForceTrustProxy1' => false,
|
||||
'sExpectedAppRootUrl1' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy2' => true,
|
||||
'sExpectedAppRootUrl2' => 'https://proxy.com:4443/',
|
||||
'bForceTrustProxy3' => false,
|
||||
'sExpectedAppRootUrl3' => 'https://proxy.com:4443/',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @dataProvider GetAbsoluteUrlAppRootPersistency
|
||||
*/
|
||||
public function testGetAbsoluteUrlAppRootPersistency($bBehindReverseProxy,$bForceTrustProxy1 ,$sExpectedAppRootUrl1,$bForceTrustProxy2 , $sExpectedAppRootUrl2,$bForceTrustProxy3 , $sExpectedAppRootUrl3)
|
||||
{
|
||||
utils::GetConfig()->Set('behind_reverse_proxy', $bBehindReverseProxy);
|
||||
utils::GetConfig()->Set('app_root_url', '');
|
||||
|
||||
//should match http://example.com/ when not trusting the proxy
|
||||
//should match https://proxy.com:4443/ when trusting the proxy
|
||||
$_SERVER = [
|
||||
'REMOTE_ADDR' => '127.0.0.1', //is not set, disable IsProxyTrusted
|
||||
'SERVER_NAME' => 'example.com',
|
||||
'SERVER_PORT' => '80',
|
||||
'REQUEST_URI' => '/index.php?baz=1',
|
||||
'SCRIPT_NAME' => '/index.php',
|
||||
'SCRIPT_FILENAME' => APPROOT.'index.php',
|
||||
'QUERY_STRING' => 'baz=1',
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
'HTTPS' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($sExpectedAppRootUrl1, utils::GetAbsoluteUrlAppRoot($bForceTrustProxy1));
|
||||
|
||||
$this->assertEquals($sExpectedAppRootUrl2, utils::GetAbsoluteUrlAppRoot($bForceTrustProxy2));
|
||||
|
||||
$this->assertEquals($sExpectedAppRootUrl3, utils::GetAbsoluteUrlAppRoot($bForceTrustProxy3));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @dataProvider GetDefaultUrlAppRootProvider
|
||||
*/
|
||||
public function testGetDefaultUrlAppRoot($bForceTrustProxy, $bConfTrustProxy, $aServerVars, $sExpectedAppRootUrl)
|
||||
{
|
||||
$_SERVER = $aServerVars;
|
||||
utils::GetConfig()->Set('behind_reverse_proxy', $bConfTrustProxy);
|
||||
$sAppRootUrl = utils::GetDefaultUrlAppRoot($bForceTrustProxy);
|
||||
$this->assertEquals($sExpectedAppRootUrl, $sAppRootUrl);
|
||||
}
|
||||
|
||||
public function GetDefaultUrlAppRootProvider()
|
||||
{
|
||||
$this->setUp();
|
||||
|
||||
$baseServerVar = [
|
||||
'REMOTE_ADDR' => '127.0.0.1', //is not set, disable IsProxyTrusted
|
||||
'SERVER_NAME' => 'example.com',
|
||||
'HTTP_X_FORWARDED_HOST' => null,
|
||||
'SERVER_PORT' => '80',
|
||||
'HTTP_X_FORWARDED_PORT' => null,
|
||||
'REQUEST_URI' => '/index.php?baz=1',
|
||||
'SCRIPT_NAME' => '/index.php',
|
||||
'SCRIPT_FILENAME' => APPROOT.'index.php',
|
||||
'QUERY_STRING' => 'baz=1',
|
||||
'HTTP_X_FORWARDED_PROTO' => null,
|
||||
'HTTP_X_FORWARDED_PROTOCOL' => null,
|
||||
'HTTPS' => null,
|
||||
];
|
||||
|
||||
return [
|
||||
'no proxy, http' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, []),
|
||||
'sExpectedAppRootUrl' => 'http://example.com/',
|
||||
],
|
||||
'no proxy, subPath, http' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'REQUEST_URI' => '/foo/index.php?baz=1',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'http://example.com/foo/',
|
||||
],
|
||||
'IIS lack REQUEST_URI' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'REQUEST_URI' => null,
|
||||
'SCRIPT_NAME' => '/foo/index.php',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'http://example.com/foo/',
|
||||
],
|
||||
'no proxy, https' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'SERVER_PORT' => '443',
|
||||
'HTTPS' => 'on',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://example.com/',
|
||||
],
|
||||
'no proxy, https on 4443' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'SERVER_PORT' => '4443',
|
||||
'HTTPS' => 'on',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://example.com:4443/',
|
||||
],
|
||||
'with proxy, not enabled' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'http://example.com/',
|
||||
],
|
||||
'with proxy, enabled HTTP_X_FORWARDED_PROTO' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => true,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'with proxy, enabled - alt HTTP_X_FORWARDED_PROTO COL' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => true,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTOCOL' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'with proxy, disabled, forced' => [
|
||||
'bForceTrustProxy' => true,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'with proxy, enabled, forced' => [
|
||||
'bForceTrustProxy' => true,
|
||||
'bConfTrustProxy' => true,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://proxy.com:4443/',
|
||||
],
|
||||
|
||||
'with proxy, disabled, forced, no remote addr' => [
|
||||
'bForceTrustProxy' => true,
|
||||
'bConfTrustProxy' => false,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'REMOTE_ADDR' => null,
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'https://proxy.com:4443/',
|
||||
],
|
||||
'with proxy, enabled, no remote addr' => [
|
||||
'bForceTrustProxy' => false,
|
||||
'bConfTrustProxy' => true,
|
||||
'aServerVars' => array_merge($baseServerVar, [
|
||||
'REMOTE_ADDR' => null,
|
||||
'HTTP_X_FORWARDED_HOST' => 'proxy.com',
|
||||
'HTTP_X_FORWARDED_PORT' => '4443',
|
||||
'HTTP_X_FORWARDED_PROTO' => 'https',
|
||||
]),
|
||||
'sExpectedAppRootUrl' => 'http://example.com/',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ToCamelCaseProvider
|
||||
* @covers utils::ToCamelCase
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sExpectedOutput
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testToCamelCase(string $sInput, string $sExpectedOutput)
|
||||
{
|
||||
$sTestedOutput = utils::ToCamelCase($sInput);
|
||||
$this->assertEquals($sExpectedOutput, $sTestedOutput, "Camel case transformation for '$sInput' doesn't match. Got '$sTestedOutput', expected '$sExpectedOutput'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
* @return \string[][]
|
||||
*/
|
||||
public function ToCamelCaseProvider(): array
|
||||
{
|
||||
return [
|
||||
'One word' => [
|
||||
'hello',
|
||||
'Hello',
|
||||
],
|
||||
'Two words separated with space' => [
|
||||
'hello world',
|
||||
'HelloWorld',
|
||||
],
|
||||
'Two words separated with underscore' => [
|
||||
'hello_world',
|
||||
'HelloWorld',
|
||||
],
|
||||
'Two words separated with dash' => [
|
||||
'hello-world',
|
||||
'HelloWorld',
|
||||
],
|
||||
'Two words separated with dot' => [
|
||||
'hello.world',
|
||||
'Hello.world',
|
||||
],
|
||||
'Three words separated with underscore and space' => [
|
||||
'hello_there world',
|
||||
'HelloThereWorld',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ToSnakeCaseProvider
|
||||
* @covers utils::ToSnakeCase
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sExpectedOutput
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testToSnakeCase(string $sInput, string $sExpectedOutput)
|
||||
{
|
||||
$sTestedOutput = utils::ToSnakeCase($sInput);
|
||||
$this->assertEquals($sExpectedOutput, $sTestedOutput, "Snake case transformation for '$sInput' doesn't match. Got '$sTestedOutput', expected '$sExpectedOutput'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
* @return \string[][]
|
||||
*/
|
||||
public function ToSnakeCaseProvider(): array
|
||||
{
|
||||
return [
|
||||
'One word lowercase' => [
|
||||
'hello',
|
||||
'hello',
|
||||
],
|
||||
'One word uppercase' => [
|
||||
'HELLO',
|
||||
'hello',
|
||||
],
|
||||
'One word capitalize' => [
|
||||
'Hello',
|
||||
'hello',
|
||||
],
|
||||
'Two words separated with space' => [
|
||||
'hello world',
|
||||
'hello_world',
|
||||
],
|
||||
'Two words separated with underscore' => [
|
||||
'hello_world',
|
||||
'hello_world',
|
||||
],
|
||||
'Two words separated with dash' => [
|
||||
'hello-world',
|
||||
'hello_world',
|
||||
],
|
||||
'Two words separated with dot' => [
|
||||
'hello.world',
|
||||
'hello_world',
|
||||
],
|
||||
'Two words camel cased' => [
|
||||
'HelloWorld',
|
||||
'hello_world',
|
||||
],
|
||||
'Two words camel cased with acronym' => [
|
||||
'HTMLWorld',
|
||||
'html_world',
|
||||
],
|
||||
'Three words separated with underscore and space' => [
|
||||
'hello_there world',
|
||||
'hello_there_world',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ToAcronymProvider
|
||||
* @covers utils::ToAcronym
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sExceptedAcronym
|
||||
*/
|
||||
public function testToAcronym(string $sInput, string $sExceptedAcronym)
|
||||
{
|
||||
$sTestedAcronym = utils::ToAcronym($sInput);
|
||||
$this->assertEquals($sExceptedAcronym, $sTestedAcronym, "Acronym for '$sInput' doesn't match. Got '$sTestedAcronym', expected '$sExceptedAcronym'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function ToAcronymProvider()
|
||||
{
|
||||
return [
|
||||
'One word, upper case letter' => [
|
||||
'Carrie',
|
||||
'C',
|
||||
],
|
||||
'One word, lower case letter' => [
|
||||
'carrie',
|
||||
'C',
|
||||
],
|
||||
'Application name' => [
|
||||
'iTop',
|
||||
'I',
|
||||
],
|
||||
'Several words, upper case letters' => [
|
||||
'Carrie Ann Moss',
|
||||
'CAM',
|
||||
],
|
||||
'Several words, mixed case letters' => [
|
||||
'My name My name',
|
||||
'MM',
|
||||
],
|
||||
'Several words, upper case letters, two first hyphened' => [
|
||||
'Lily-Rose Depp',
|
||||
'LRD',
|
||||
],
|
||||
'Several words, mixed case letters, two first hyphened' => [
|
||||
'Lily-rose Depp',
|
||||
'LD',
|
||||
],
|
||||
'Several words, upper case letetrs, two last hypened' => [
|
||||
'Jada Pinkett-Smith',
|
||||
'JPS',
|
||||
],
|
||||
'Several words, mixed case letters, two last hyphened' => [
|
||||
'Jada Pinkett-smith',
|
||||
'JP',
|
||||
],
|
||||
'Several words, cyrillic alphabet' => [
|
||||
'Денис Александра',
|
||||
'ДА',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider GetMentionedObjectsFromTextProvider
|
||||
* @covers utils::GetMentionedObjectsFromText
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sFormat
|
||||
* @param array $aExceptedMentionedObjects
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testGetMentionedObjectsFromText(string $sInput, string $sFormat, array $aExceptedMentionedObjects)
|
||||
{
|
||||
$aTestedMentionedObjects = utils::GetMentionedObjectsFromText($sInput, $sFormat);
|
||||
|
||||
$sExpectedAsString = print_r($aExceptedMentionedObjects, true);
|
||||
$sTestedAsString = print_r($aTestedMentionedObjects, true);
|
||||
|
||||
$this->assertEquals($sTestedAsString, $sExpectedAsString, "Found mentioned objects don't match. Got: $sTestedAsString, expected $sExpectedAsString");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetMentionedObjectsFromTextProvider(): array
|
||||
{
|
||||
$sAbsUrlAppRoot = utils::GetAbsoluteUrlAppRoot();
|
||||
|
||||
return [
|
||||
'No object' => [
|
||||
"Begining
|
||||
Second line
|
||||
End",
|
||||
utils::ENUM_TEXT_FORMAT_HTML,
|
||||
[],
|
||||
],
|
||||
'1 UserRequest' => [
|
||||
"Begining
|
||||
Before link <a href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=UserRequest&id=12345&foo=bar\">R-012345</a> After link
|
||||
End",
|
||||
utils::ENUM_TEXT_FORMAT_HTML,
|
||||
[
|
||||
'UserRequest' => ['12345'],
|
||||
],
|
||||
],
|
||||
'2 UserRequests' => [
|
||||
"Begining
|
||||
Before link <a href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=UserRequest&id=12345&foo=bar\">R-012345</a> After link
|
||||
And <a href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=UserRequest&id=987654&foo=bar\">R-987654</a>
|
||||
End",
|
||||
utils::ENUM_TEXT_FORMAT_HTML,
|
||||
[
|
||||
'UserRequest' => ['12345', '987654'],
|
||||
],
|
||||
],
|
||||
'1 UserRequest, 1 Person' => [
|
||||
"Begining
|
||||
Before link <a href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=UserRequest&id=12345&foo=bar\">R-012345</a> After link
|
||||
And <a href=\"$sAbsUrlAppRoot/pages/UI.php&operation=details&class=Person&id=3&foo=bar\">Claude Monet</a>
|
||||
End",
|
||||
utils::ENUM_TEXT_FORMAT_HTML,
|
||||
[
|
||||
'UserRequest' => ['12345'],
|
||||
'Person' => ['3'],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider FormatInitialsForMedallionProvider
|
||||
* @covers utils::FormatInitialsForMedallion
|
||||
*
|
||||
* @param string $sInput
|
||||
* @param string $sExpected
|
||||
*/
|
||||
public function testFormatInitialsForMedallion(string $sInput, string $sExpected)
|
||||
{
|
||||
$sTested = utils::FormatInitialsForMedallion($sInput);
|
||||
$this->assertEquals($sExpected, $sTested);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public function FormatInitialsForMedallionProvider()
|
||||
{
|
||||
return [
|
||||
'All letters kept (2)' => [
|
||||
'AB',
|
||||
'AB',
|
||||
],
|
||||
'All letters kept (3)' => [
|
||||
'ABC',
|
||||
'ABC',
|
||||
],
|
||||
'Only 3 first letters kept (4)' => [
|
||||
'ABCD',
|
||||
'ABC',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sExpressionToConvert
|
||||
* @param int $iExpectedConvertedValue
|
||||
*
|
||||
* @dataProvider ConvertToBytesProvider
|
||||
*/
|
||||
public function testConvertToBytes($sExpressionToConvert, $iExpectedConvertedValue)
|
||||
{
|
||||
$iCurrentConvertedValue = utils::ConvertToBytes($sExpressionToConvert);
|
||||
self::assertEquals($iExpectedConvertedValue, $iCurrentConvertedValue, 'Converted value wasn\'t the one expected !');
|
||||
self::assertSame($iExpectedConvertedValue, $iCurrentConvertedValue, 'Value was converted but not of the expected type');
|
||||
}
|
||||
|
||||
public function ConvertToBytesProvider()
|
||||
{
|
||||
return [
|
||||
'123 int value' => ['123', 123],
|
||||
'-1 no limit' => ['-1', -1],
|
||||
'56k' => ['56k', 56 * 1024],
|
||||
'512M' => ['512M', 512 * 1024 * 1024],
|
||||
'2G' => ['2G', 2 * 1024 * 1024 * 1024],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sString
|
||||
* @param int $iExpected
|
||||
*
|
||||
* @dataProvider StrLenProvider
|
||||
*/
|
||||
public function testStrLen(?string $sString, int $iExpected)
|
||||
{
|
||||
$iComputed = utils::StrLen($sString);
|
||||
self::assertEquals($iExpected, $iComputed, 'Length was not as expected');
|
||||
}
|
||||
|
||||
public function StrLenProvider(): array
|
||||
{
|
||||
return [
|
||||
'null value' => [null, 0],
|
||||
'0 character' => ['', 0],
|
||||
'1 character' => ['a', 1],
|
||||
'5 characters' => ['abcde', 5],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sanitizer.
|
||||
*
|
||||
* @param $type string type of sanitizer
|
||||
* @param $valueToSanitize ? value to sanitize
|
||||
* @param $expectedResult ? expected result
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @dataProvider sanitizerDataProvider
|
||||
*/
|
||||
public function testSanitizer($type, $valueToSanitize, $expectedResult)
|
||||
{
|
||||
$this->assertEquals($expectedResult, utils::Sanitize($valueToSanitize, null, $type), 'url sanitize failed');
|
||||
}
|
||||
|
||||
/**
|
||||
* DataProvider for testSanitizer
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sanitizerDataProvider()
|
||||
{
|
||||
return [
|
||||
'good integer' => [utils::ENUM_SANITIZATION_FILTER_INTEGER, '2565', '2565'],
|
||||
'bad integer' => [utils::ENUM_SANITIZATION_FILTER_INTEGER, 'a2656', '2656'],
|
||||
/**
|
||||
* 'class' filter needs a loaded datamodel... and is only an indirection to \MetaModel::IsValidClass so might very important to test !
|
||||
* If we switch this class to ItopDataTestCase then we are seeing :
|
||||
* - the class now takes 18s to process instead of... 459ms when using ItopTestCase !!!
|
||||
* - multiple errors are thrown in testGetAbsoluteUrlAppRootPersistency :(
|
||||
* We decided it wasn't worse the effort to test the 'class' filter !
|
||||
*/
|
||||
// 'good class' => ['class', 'UserRequest', 'UserRequest'],
|
||||
// 'bad class' => ['class', 'MyUserRequest',null],
|
||||
'good string' => [utils::ENUM_SANITIZATION_FILTER_STRING, 'Is Peter smart and funny?', 'Is Peter smart and funny?'],
|
||||
'bad string' => [utils::ENUM_SANITIZATION_FILTER_STRING, 'Is Peter <smart> & funny?', 'Is Peter <smart> & funny?'],
|
||||
'good transaction_id' => [utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID, '8965.-dd', '8965.-dd'],
|
||||
'bad transaction_id' => [utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID, '8965.-dd+', null],
|
||||
'good route' => [utils::ENUM_SANITIZATION_FILTER_ROUTE, 'object.modify', 'object.modify'],
|
||||
'good route with underscore' => [utils::ENUM_SANITIZATION_FILTER_ROUTE, 'object.apply_modify', 'object.apply_modify'],
|
||||
'bad route with space' => [utils::ENUM_SANITIZATION_FILTER_ROUTE, 'object modify', null],
|
||||
'good operation' => [utils::ENUM_SANITIZATION_FILTER_OPERATION, 'modify', 'modify'],
|
||||
'good operation with underscore' => [utils::ENUM_SANITIZATION_FILTER_OPERATION, 'apply_modify', 'apply_modify'],
|
||||
'bad operation with space' => [utils::ENUM_SANITIZATION_FILTER_OPERATION, 'apply modify', null],
|
||||
'good parameter' => [utils::ENUM_SANITIZATION_FILTER_PARAMETER, 'JU8965-dd=_', 'JU8965-dd=_'],
|
||||
'bad parameter' => [utils::ENUM_SANITIZATION_FILTER_PARAMETER, '8965.-dd+', null],
|
||||
'good field_name' => [utils::ENUM_SANITIZATION_FILTER_FIELD_NAME, 'Name->bUzz38', 'Name->bUzz38'],
|
||||
'bad field_name' => [utils::ENUM_SANITIZATION_FILTER_FIELD_NAME, 'name-buzz', null],
|
||||
'good context_param' => [utils::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM, '%dssD25_=%:+-', '%dssD25_=%:+-'],
|
||||
'bad context_param' => [utils::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM, '%dssD,25_=%:+-', null],
|
||||
'good element_identifier' => [utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER, 'AD05nb', 'AD05nb'],
|
||||
'bad element_identifier' => [utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER, 'AD05nb+', 'AD05nb'],
|
||||
'good url' => [utils::ENUM_SANITIZATION_FILTER_URL, 'https://www.w3schools.com', 'https://www.w3schools.com'],
|
||||
'bad url' => [utils::ENUM_SANITIZATION_FILTER_URL, 'https://www.w3schoo<6F><6F>ls.co<63>m', 'https://www.w3schools.com'],
|
||||
'raw_data' => ['raw_data', '<Test>\s😃😃😃', '<Test>\s😃😃😃'],
|
||||
];
|
||||
}
|
||||
}
|
||||
91
tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use ActionEmail;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
* @covers \ActionEmail
|
||||
*/
|
||||
class ActionEmailTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
const CREATE_TEST_ORG = true;
|
||||
|
||||
/** @var \ActionEmail|null Temp ActionEmail created for tests */
|
||||
protected static $oActionEmail = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
static::$oActionEmail = MetaModel::NewObject('ActionEmail', [
|
||||
'name' => 'Test action',
|
||||
'status' => 'disabled',
|
||||
'from' => 'unit-test@openitop.org',
|
||||
'subject' => 'Test subject',
|
||||
'body' => 'Test body',
|
||||
]);
|
||||
static::$oActionEmail->DBInsert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \ActionEmail::GenerateIdentifierForHeaders
|
||||
* @dataProvider GenerateIdentifierForHeadersProvider
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testGenerateIdentifierForHeaders(string $sHeaderName)
|
||||
{
|
||||
// Retrieve object
|
||||
$oObject = MetaModel::GetObject('Organization', $this->getTestOrgId(), true, true);
|
||||
$sObjClass = get_class($oObject);
|
||||
$sObjId = $oObject->GetKey();
|
||||
|
||||
try {
|
||||
$sTestedIdentifier = $this->InvokeNonPublicMethod('\ActionEmail', 'GenerateIdentifierForHeaders', static::$oActionEmail, [$oObject, $sHeaderName]);
|
||||
} catch (Exception $oException) {
|
||||
$sTestedIdentifier = null;
|
||||
}
|
||||
|
||||
$sAppName = utils::Sanitize(ITOP_APPLICATION_SHORT, '', utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME);
|
||||
$sEnvironmentHash = MetaModel::GetEnvironmentId();
|
||||
|
||||
switch ($sHeaderName) {
|
||||
case ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID:
|
||||
// Note: For this test we can't use the more readable sprintf test as the generated timestamp will never be the same as the one generated during the call of the tested method
|
||||
// $sTimestamp = microtime(true /* get as float*/);
|
||||
// $sExpectedIdentifier = sprintf('%s_%s_%d_%f@%s.openitop.org', $sAppName, $sObjClass, $sObjId, $sTimestamp, $sEnvironmentHash);
|
||||
$this->assertEquals(1, preg_match('/'.$sAppName.'_'.$sObjClass.'_'.$sObjId.'_[\d]+\.[\d]+@'.$sEnvironmentHash.'.openitop.org/', $sTestedIdentifier), "Identifier doesn't match regexp for header $sHeaderName, got $sTestedIdentifier");
|
||||
break;
|
||||
|
||||
case ActionEmail::ENUM_HEADER_NAME_REFERENCES:
|
||||
$sExpectedIdentifier = '<'.sprintf('%s_%s_%d@%s.openitop.org', $sAppName, $sObjClass, $sObjId, $sEnvironmentHash).'>';
|
||||
$this->assertEquals($sExpectedIdentifier, $sTestedIdentifier);
|
||||
break;
|
||||
|
||||
default:
|
||||
$sExpectedIdentifier = null;
|
||||
$this->assertEquals($sExpectedIdentifier, $sTestedIdentifier);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function GenerateIdentifierForHeadersProvider()
|
||||
{
|
||||
return [
|
||||
'Message-ID' => ['Message-ID'],
|
||||
'References' => ['References'],
|
||||
'IncorrectHeaderName' => ['IncorrectHeaderName'],
|
||||
];
|
||||
}
|
||||
}
|
||||