test.class.inc.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. <?php
  2. // Copyright (C) 2010 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * Core automated tests - basics
  18. *
  19. * @author Erwan Taloc <erwan.taloc@combodo.com>
  20. * @author Romain Quetiez <romain.quetiez@combodo.com>
  21. * @author Denis Flaven <denis.flaven@combodo.com>
  22. * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
  23. */
  24. require_once(APPROOT.'/core/coreexception.class.inc.php');
  25. require_once(APPROOT.'/core/attributedef.class.inc.php');
  26. require_once(APPROOT.'/core/filterdef.class.inc.php');
  27. require_once(APPROOT.'/core/stimulus.class.inc.php');
  28. require_once(APPROOT.'/core/MyHelpers.class.inc.php');
  29. require_once(APPROOT.'/core/expression.class.inc.php');
  30. require_once(APPROOT.'/core/cmdbsource.class.inc.php');
  31. require_once(APPROOT.'/core/sqlquery.class.inc.php');
  32. require_once(APPROOT.'/core/log.class.inc.php');
  33. require_once(APPROOT.'/core/kpi.class.inc.php');
  34. require_once(APPROOT.'/core/dbobject.class.php');
  35. require_once(APPROOT.'/core/dbobjectsearch.class.php');
  36. require_once(APPROOT.'/core/dbobjectset.class.php');
  37. require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
  38. require_once(APPROOT.'/core/userrights.class.inc.php');
  39. require_once(APPROOT.'/webservices/webservices.class.inc.php');
  40. // Just to differentiate programmatically triggered exceptions and other kind of errors (usefull?)
  41. class UnitTestException extends Exception
  42. {}
  43. /**
  44. * Improved display of the backtrace
  45. *
  46. * @package iTopORM
  47. */
  48. class ExceptionFromError extends Exception
  49. {
  50. public function getTraceAsHtml()
  51. {
  52. $aBackTrace = $this->getTrace();
  53. return MyHelpers::get_callstack_html(0, $this->getTrace());
  54. // return "<pre>\n".$this->getTraceAsString()."</pre>\n";
  55. }
  56. }
  57. /**
  58. * Test handler API and basic helpers
  59. *
  60. * @package iTopORM
  61. */
  62. abstract class TestHandler
  63. {
  64. protected $m_aSuccesses;
  65. protected $m_aWarnings;
  66. protected $m_aErrors;
  67. protected $m_sOutput;
  68. public function __construct()
  69. {
  70. $this->m_aSuccesses = array();
  71. $this->m_aWarnings = array();
  72. $this->m_aErrors = array();
  73. }
  74. static public function GetName() {return "fooname";}
  75. static public function GetDescription(){return "foodesc";}
  76. protected function DoPrepare() {return true;}
  77. abstract protected function DoExecute();
  78. protected function DoCleanup() {return true;}
  79. protected static function DumpVariable($var)
  80. {
  81. echo "<pre class=\"vardump\">\n";
  82. print_r($var);
  83. echo "</pre>\n";
  84. }
  85. protected function ReportSuccess($sMessage, $sSubtestId = '')
  86. {
  87. $this->m_aSuccesses[] = $sMessage;
  88. }
  89. protected function ReportWarning($sMessage, $sSubtestId = '')
  90. {
  91. $this->m_aWarnings[] = $sMessage;
  92. }
  93. protected function ReportError($sMessage, $sSubtestId = '')
  94. {
  95. $this->m_aErrors[] = $sMessage;
  96. }
  97. public function GetResults()
  98. {
  99. return $this->m_aSuccesses;
  100. }
  101. public function GetWarnings()
  102. {
  103. return $this->m_aWarnings;
  104. }
  105. public function GetErrors()
  106. {
  107. return $this->m_aErrors;
  108. }
  109. public function GetOutput()
  110. {
  111. return $this->m_sOutput;
  112. }
  113. public function error_handler($errno, $errstr, $errfile, $errline)
  114. {
  115. // Note: return false to call the default handler (stop the program if an error)
  116. switch ($errno)
  117. {
  118. case E_USER_ERROR:
  119. $this->ReportError($errstr);
  120. //throw new ExceptionFromError("Fatal error in line $errline of file $errfile: $errstr");
  121. break;
  122. case E_USER_WARNING:
  123. $this->ReportWarning($errstr);
  124. break;
  125. case E_USER_NOTICE:
  126. $this->ReportWarning($errstr);
  127. break;
  128. default:
  129. $this->ReportWarning("Unknown error type: [$errno] $errstr in $errfile at $errline");
  130. echo "Unknown error type: [$errno] $errstr in $errfile at $errline<br />\n";
  131. break;
  132. }
  133. return true; // do not call the default handler
  134. }
  135. public function Execute()
  136. {
  137. ob_start();
  138. set_error_handler(array($this, 'error_handler'));
  139. try
  140. {
  141. $this->DoPrepare();
  142. $this->DoExecute();
  143. }
  144. catch (ExceptionFromError $e)
  145. {
  146. $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
  147. }
  148. catch (CoreException $e)
  149. {
  150. //$this->ReportError($e->getMessage());
  151. //$this->ReportError($e->__tostring());
  152. $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
  153. }
  154. catch (Exception $e)
  155. {
  156. //$this->ReportError($e->getMessage());
  157. //$this->ReportError($e->__tostring());
  158. $this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString());
  159. }
  160. restore_error_handler();
  161. $this->m_sOutput = ob_get_clean();
  162. return (count($this->GetErrors()) == 0);
  163. }
  164. static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = 'admin', $sOptionnalHeaders = null)
  165. {
  166. $aDataAndAuth = $aData;
  167. // To be changed to use basic authentication
  168. $aDataAndAuth['operation'] = 'login';
  169. $aDataAndAuth['auth_user'] = $sLogin;
  170. $aDataAndAuth['auth_pwd'] = $sPassword;
  171. $sHost = $_SERVER['HTTP_HOST'];
  172. $sRawPath = $_SERVER['SCRIPT_NAME'];
  173. $sPath = dirname($sRawPath);
  174. $sUrl = "http://$sHost/$sPath/$sRelativeUrl";
  175. return self::DoPostRequest($sUrl, $aDataAndAuth, $sOptionnalHeaders);
  176. }
  177. // Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
  178. // originaly named after do_post_request
  179. // Partially adapted to our coding conventions
  180. static protected function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null)
  181. {
  182. // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
  183. $sData = http_build_query($aData);
  184. $aParams = array('http' => array(
  185. 'method' => 'POST',
  186. 'content' => $sData,
  187. 'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
  188. ));
  189. if ($sOptionnalHeaders !== null)
  190. {
  191. $aParams['http']['header'] .= $sOptionnalHeaders;
  192. }
  193. $ctx = stream_context_create($aParams);
  194. $fp = @fopen($sUrl, 'rb', false, $ctx);
  195. if (!$fp)
  196. {
  197. global $php_errormsg;
  198. if (isset($php_errormsg))
  199. {
  200. throw new Exception("Problem with $sUrl, $php_errormsg");
  201. }
  202. else
  203. {
  204. throw new Exception("Problem with $sUrl");
  205. }
  206. }
  207. $response = @stream_get_contents($fp);
  208. if ($response === false)
  209. {
  210. throw new Exception("Problem reading data from $sUrl, $php_errormsg");
  211. }
  212. return $response;
  213. }
  214. }
  215. /**
  216. * Test to execute a piece of code (checks if an error occurs)
  217. *
  218. * @package iTopORM
  219. */
  220. abstract class TestFunction extends TestHandler
  221. {
  222. // simply overload DoExecute (temporary)
  223. }
  224. /**
  225. * Test to execute a piece of code (checks if an error occurs)
  226. *
  227. * @package iTopORM
  228. */
  229. abstract class TestWebServices extends TestHandler
  230. {
  231. }
  232. /**
  233. * Test to execute a piece of code (checks if an error occurs)
  234. *
  235. * @package iTopORM
  236. */
  237. abstract class TestSoapWebService extends TestHandler
  238. {
  239. // simply overload DoExecute (temporary)
  240. function __construct()
  241. {
  242. parent::__construct();
  243. }
  244. }
  245. /**
  246. * Test to check that a function outputs some values depending on its input
  247. *
  248. * @package iTopORM
  249. */
  250. abstract class TestFunctionInOut extends TestFunction
  251. {
  252. // abstract static public function GetCallSpec(); // parameters to call_user_func
  253. // abstract static public function GetInOut(); // array of input => output
  254. protected function DoExecute()
  255. {
  256. $aTests = $this->GetInOut();
  257. if (is_array($aTests))
  258. {
  259. foreach ($aTests as $iTestId => $aTest)
  260. {
  261. $ret = call_user_func_array($this->GetCallSpec(), $aTest['args']);
  262. if ($ret != $aTest['output'])
  263. {
  264. // Note: to be improved to cope with non string parameters
  265. $this->ReportError("Found '$ret' while expecting '".$aTest['output']."'", $iTestId);
  266. }
  267. else
  268. {
  269. $this->ReportSuccess("Found the expected output '$ret'", $iTestId);
  270. }
  271. }
  272. }
  273. else
  274. {
  275. $ret = call_user_func($this->GetCallSpec());
  276. $this->ReportSuccess('Finished successfully');
  277. }
  278. }
  279. }
  280. /**
  281. * Test to check an URL (Searches for Error/Warning/Etc keywords)
  282. *
  283. * @package iTopORM
  284. */
  285. abstract class TestUrl extends TestHandler
  286. {
  287. // abstract static public function GetUrl();
  288. // abstract static public function GetErrorKeywords();
  289. // abstract static public function GetWarningKeywords();
  290. protected function DoExecute()
  291. {
  292. return true;
  293. }
  294. }
  295. /**
  296. * Test to check a user management module
  297. *
  298. * @package iTopORM
  299. */
  300. abstract class TestUserRights extends TestHandler
  301. {
  302. protected function DoExecute()
  303. {
  304. return true;
  305. }
  306. }
  307. /**
  308. * Test to execute a scenario on a given DB
  309. *
  310. * @package iTopORM
  311. */
  312. abstract class TestScenarioOnDB extends TestHandler
  313. {
  314. // abstract static public function GetDBHost();
  315. // abstract static public function GetDBUser();
  316. // abstract static public function GetDBPwd();
  317. // abstract static public function GetDBName();
  318. protected function DoPrepare()
  319. {
  320. $sDBHost = $this->GetDBHost();
  321. $sDBUser = $this->GetDBUser();
  322. $sDBPwd = $this->GetDBPwd();
  323. $sDBName = $this->GetDBName();
  324. CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd);
  325. CMDBSource::SetCharacterSet();
  326. if (CMDBSource::IsDB($sDBName))
  327. {
  328. CMDBSource::DropDB($sDBName);
  329. }
  330. CMDBSource::CreateDB($sDBName);
  331. }
  332. protected function DoCleanup()
  333. {
  334. // CMDBSource::DropDB($this->GetDBName());
  335. }
  336. }
  337. /**
  338. * Test to use a business model on a given DB
  339. *
  340. * @package iTopORM
  341. */
  342. abstract class TestBizModel extends TestHandler
  343. {
  344. // abstract static public function GetDBSubName();
  345. // abstract static public function GetBusinessModelFile();
  346. // abstract static public function GetConfigFile();
  347. protected function DoPrepare()
  348. {
  349. $sConfigFile = APPROOT.$this->GetConfigFile();
  350. MetaModel::Startup($sConfigFile);
  351. // #@# Temporary disabled by Romain
  352. // MetaModel::CheckDefinitions();
  353. // something here to create records... but that's another story
  354. }
  355. protected $m_oChange;
  356. protected function GetCurrentChange()
  357. {
  358. if (!isset($this->m_oChange))
  359. {
  360. new CMDBChange();
  361. $oMyChange = MetaModel::NewObject("CMDBChange");
  362. $oMyChange->Set("date", time());
  363. $oMyChange->Set("userinfo", "Someone doing some tests");
  364. $iChangeId = $oMyChange->DBInsertNoReload();
  365. $this->m_oChange = $oMyChange;
  366. }
  367. return $this->m_oChange;
  368. }
  369. protected function ObjectToDB($oNew, $bReload = false)
  370. {
  371. // list($bRes, $aIssues) = $oNew->CheckToWrite();
  372. // if (!$bRes)
  373. // {
  374. // throw new CoreException('Could not create object, unexpected values', array('issues' => $aIssues));
  375. // }
  376. if ($oNew instanceof CMDBObject)
  377. {
  378. if (!isset($this->m_oChange))
  379. {
  380. new CMDBChange();
  381. $oMyChange = MetaModel::NewObject("CMDBChange");
  382. $oMyChange->Set("date", time());
  383. $oMyChange->Set("userinfo", "Someone doing some tests");
  384. $iChangeId = $oMyChange->DBInsertNoReload();
  385. $this->m_oChange = $oMyChange;
  386. }
  387. $oChange = $this->GetCurrentChange();
  388. if ($bReload)
  389. {
  390. $iId = $oNew->DBInsertTracked($oChange);
  391. }
  392. else
  393. {
  394. $iId = $oNew->DBInsertTrackedNoReload($oChange);
  395. }
  396. }
  397. else
  398. {
  399. if ($bReload)
  400. {
  401. $iId = $oNew->DBInsert();
  402. }
  403. else
  404. {
  405. $iId = $oNew->DBInsertNoReload();
  406. }
  407. }
  408. return $iId;
  409. }
  410. protected function UpdateObjectInDB($oObject)
  411. {
  412. if ($oObject instanceof CMDBObject)
  413. {
  414. $oChange = $this->GetCurrentChange();
  415. $oObject->DBUpdateTracked($oChange);
  416. }
  417. else
  418. {
  419. $oObject->DBUpdate();
  420. }
  421. }
  422. protected function ResetDB()
  423. {
  424. if (MetaModel::DBExists(false))
  425. {
  426. MetaModel::DBDrop();
  427. }
  428. MetaModel::DBCreate();
  429. }
  430. static protected function show_list($oObjectSet)
  431. {
  432. $oObjectSet->Rewind();
  433. $aData = array();
  434. while ($oItem = $oObjectSet->Fetch())
  435. {
  436. $aValues = array();
  437. foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode)
  438. {
  439. $aValues[$sAttCode] = $oItem->GetAsHTML($sAttCode);
  440. }
  441. //echo $oItem->GetKey()." => ".implode(", ", $aValues)."</br>\n";
  442. $aData[] = $aValues;
  443. }
  444. echo MyHelpers::make_table_from_assoc_array($aData);
  445. }
  446. static protected function search_and_show_list(DBObjectSearch $oMyFilter)
  447. {
  448. $oObjSet = new CMDBObjectSet($oMyFilter);
  449. echo $oMyFilter->__DescribeHTML()."' - Found ".$oObjSet->Count()." items.</br>\n";
  450. self::show_list($oObjSet);
  451. }
  452. static protected function search_and_show_list_from_oql($sOQL)
  453. {
  454. echo $sOQL."...<br/>\n";
  455. $oNewFilter = DBObjectSearch::FromOQL($sOQL);
  456. self::search_and_show_list($oNewFilter);
  457. }
  458. }
  459. /**
  460. * Test to execute a scenario common to any business model (tries to build all the possible queries, etc.)
  461. *
  462. * @package iTopORM
  463. */
  464. abstract class TestBizModelGeneric extends TestBizModel
  465. {
  466. static public function GetName()
  467. {
  468. return 'Full test on a given business model';
  469. }
  470. static public function GetDescription()
  471. {
  472. return 'Systematic tests: gets each and every existing class and tries every attribute, search filters, etc.';
  473. }
  474. protected function DoPrepare()
  475. {
  476. parent::DoPrepare();
  477. if (!MetaModel::DBExists(false))
  478. {
  479. MetaModel::DBCreate();
  480. }
  481. // something here to create records... but that's another story
  482. }
  483. protected function DoExecute()
  484. {
  485. foreach(MetaModel::GetClasses() as $sClassName)
  486. {
  487. if (MetaModel::HasTable($sClassName)) continue;
  488. $oNobody = MetaModel::GetObject($sClassName, 123);
  489. $oBaby = new $sClassName;
  490. $oFilter = new DBObjectSearch($sClassName);
  491. // Challenge reversibility of OQL / filter object
  492. //
  493. $sExpr1 = $oFilter->ToOQL();
  494. $oNewFilter = DBObjectSearch::FromOQL($sExpr1);
  495. $sExpr2 = $oNewFilter->ToOQL();
  496. if ($sExpr1 != $sExpr2)
  497. {
  498. $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>");
  499. }
  500. // Use the filter (perform the query)
  501. //
  502. $oSet = new CMDBObjectSet($oFilter);
  503. $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
  504. }
  505. return true;
  506. }
  507. }
  508. ?>