test.class.inc.php 15 KB

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