TYPO3 CMS  TYPO3_7-6
FunctionalTestCase.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Tests;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
20 
51 abstract class FunctionalTestCase extends BaseTestCase
52 {
70  protected $coreExtensionsToLoad = [];
71 
92  protected $testExtensionsToLoad = [];
93 
123 
131 
157 
163  protected $backendUserFixture = 'typo3/sysext/core/Tests/Functional/Fixtures/be_users.xml';
164 
170  private $bootstrapUtility = null;
171 
178  protected function getInstanceIdentifier()
179  {
181  }
182 
188  protected function getInstancePath()
189  {
191  }
192 
200  protected function setUp()
201  {
202  if (!defined('ORIGINAL_ROOT')) {
203  $this->markTestSkipped('Functional tests must be called through phpunit on CLI');
204  }
205  $this->bootstrapUtility = new FunctionalTestCaseBootstrapUtility();
206  $this->bootstrapUtility->setUp(
207  get_class($this),
208  $this->coreExtensionsToLoad,
209  $this->testExtensionsToLoad,
210  $this->pathsToLinkInTestInstance,
211  $this->configurationToUseInTestInstance,
212  $this->additionalFoldersToCreate
213  );
214  }
215 
224  protected function getDatabaseConnection()
225  {
226  return $GLOBALS['TYPO3_DB'];
227  }
228 
236  protected function setUpBackendUserFromFixture($userUid)
237  {
238  $this->importDataSet(ORIGINAL_ROOT . $this->backendUserFixture);
239  $database = $this->getDatabaseConnection();
240  $userRow = $database->exec_SELECTgetSingleRow('*', 'be_users', 'uid = ' . (int)$userUid);
241 
243  $backendUser = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class);
244  $sessionId = $backendUser->createSessionId();
245  $_COOKIE['be_typo_user'] = $sessionId;
246  $backendUser->id = $sessionId;
247  $backendUser->sendNoCacheHeaders = false;
248  $backendUser->dontSetCookie = true;
249  $backendUser->createUserSession($userRow);
250 
251  $GLOBALS['BE_USER'] = $backendUser;
252  $GLOBALS['BE_USER']->start();
253  if (!is_array($GLOBALS['BE_USER']->user) || !$GLOBALS['BE_USER']->user['uid']) {
254  throw new Exception(
255  'Can not initialize backend user',
256  1377095807
257  );
258  }
259  $GLOBALS['BE_USER']->backendCheckLogin();
260 
261  return $backendUser;
262  }
263 
271  protected function importDataSet($path)
272  {
273  if (!is_file($path)) {
274  throw new Exception(
275  'Fixture file ' . $path . ' not found',
276  1376746261
277  );
278  }
279 
280  $database = $this->getDatabaseConnection();
281 
282  $fileContent = file_get_contents($path);
283  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
284  $previousValueOfEntityLoader = libxml_disable_entity_loader(true);
285  $xml = simplexml_load_string($fileContent);
286  libxml_disable_entity_loader($previousValueOfEntityLoader);
287  $foreignKeys = [];
288 
290  foreach ($xml->children() as $table) {
291  $insertArray = [];
292 
294  foreach ($table->children() as $column) {
295  $columnName = $column->getName();
296  $columnValue = null;
297 
298  if (isset($column['ref'])) {
299  list($tableName, $elementId) = explode('#', $column['ref']);
300  $columnValue = $foreignKeys[$tableName][$elementId];
301  } elseif (isset($column['is-NULL']) && ($column['is-NULL'] === 'yes')) {
302  $columnValue = null;
303  } else {
304  $columnValue = (string)$table->$columnName;
305  }
306 
307  $insertArray[$columnName] = $columnValue;
308  }
309 
310  $tableName = $table->getName();
311  $result = $database->exec_INSERTquery($tableName, $insertArray);
312  if ($result === false) {
313  throw new Exception(
314  'Error when processing fixture file: ' . $path . ' Can not insert data to table ' . $tableName . ': ' . $database->sql_error(),
315  1376746262
316  );
317  }
318  if (isset($table['id'])) {
319  $elementId = (string)$table['id'];
320  $foreignKeys[$tableName][$elementId] = $database->sql_insert_id();
321  }
322  }
323  }
324 
331  public function importCSVDataSet($path)
332  {
333  $dataSet = DataSet::read($path, true);
334 
335  foreach ($dataSet->getTableNames() as $tableName) {
336  foreach ($dataSet->getElements($tableName) as $element) {
337  $this->getDatabaseConnection()->exec_INSERTquery(
338  $tableName,
339  $element
340  );
341  $sqlError = $this->getDatabaseConnection()->sql_error();
342  if (!empty($sqlError)) {
343  $this->fail('SQL Error for table "' . $tableName . '": ' . LF . $sqlError);
344  }
345  }
346  }
347  }
348 
354  protected function assertCSVDataSet($path)
355  {
356  $dataSet = DataSet::read($path);
357  $failMessages = [];
358 
359  foreach ($dataSet->getTableNames() as $tableName) {
360  $hasUidField = ($dataSet->getIdIndex($tableName) !== null);
361  $records = $this->getAllRecords($tableName, $hasUidField);
362  foreach ($dataSet->getElements($tableName) as $assertion) {
363  $result = $this->assertInRecords($assertion, $records);
364  if ($result === false) {
365  if ($hasUidField && empty($records[$assertion['uid']])) {
366  $failMessages[] = 'Record "' . $tableName . ':' . $assertion['uid'] . '" not found in database';
367  continue;
368  }
369  $recordIdentifier = $tableName . ($hasUidField ? ':' . $assertion['uid'] : '');
370  $additionalInformation = ($hasUidField ? $this->renderRecords($assertion, $records[$assertion['uid']]) : $this->arrayToString($assertion));
371  $failMessages[] = 'Assertion in data-set failed for "' . $recordIdentifier . '":' . LF . $additionalInformation;
372  // Unset failed asserted record
373  if ($hasUidField) {
374  unset($records[$assertion['uid']]);
375  }
376  } else {
377  // Unset asserted record
378  unset($records[$result]);
379  // Increase assertion counter
380  $this->assertTrue($result !== false);
381  }
382  }
383  if (!empty($records)) {
384  foreach ($records as $record) {
385  $recordIdentifier = $tableName . ':' . $record['uid'];
386  $emptyAssertion = array_fill_keys($dataSet->getFields($tableName), '[none]');
387  $reducedRecord = array_intersect_key($record, $emptyAssertion);
388  $additionalInformation = ($hasUidField ? $this->renderRecords($emptyAssertion, $reducedRecord) : $this->arrayToString($reducedRecord));
389  $failMessages[] = 'Not asserted record found for "' . $recordIdentifier . '":' . LF . $additionalInformation;
390  }
391  }
392  }
393 
394  if (!empty($failMessages)) {
395  $this->fail(implode(LF, $failMessages));
396  }
397  }
398 
407  protected function assertInRecords(array $expectedRecord, array $actualRecords)
408  {
409  foreach ($actualRecords as $index => $record) {
410  $differentFields = $this->getDifferentFields($expectedRecord, $record);
411 
412  if (empty($differentFields)) {
413  return $index;
414  }
415  }
416 
417  return false;
418  }
419 
428  protected function getAllRecords($tableName, $hasUidField = false)
429  {
430  $allRecords = [];
431 
432  $records = $this->getDatabaseConnection()->exec_SELECTgetRows(
433  '*',
434  $tableName,
435  '1=1',
436  '',
437  '',
438  '',
439  ($hasUidField ? 'uid' : '')
440  );
441 
442  if (!empty($records)) {
443  $allRecords = $records;
444  }
445 
446  return $allRecords;
447  }
448 
455  protected function arrayToString(array $array)
456  {
457  $elements = [];
458  foreach ($array as $key => $value) {
459  if (is_array($value)) {
460  $value = $this->arrayToString($value);
461  }
462  $elements[] = "'" . $key . "' => '" . $value . "'";
463  }
464  return 'array(' . PHP_EOL . ' ' . implode(', ' . PHP_EOL . ' ', $elements) . PHP_EOL . ')' . PHP_EOL;
465  }
466 
475  protected function renderRecords(array $assertion, array $record)
476  {
477  $differentFields = $this->getDifferentFields($assertion, $record);
478  $columns = [
479  'fields' => ['Fields'],
480  'assertion' => ['Assertion'],
481  'record' => ['Record'],
482  ];
483  $lines = [];
484  $linesFromXmlValues = [];
485  $result = '';
486 
487  foreach ($differentFields as $differentField) {
488  $columns['fields'][] = $differentField;
489  $columns['assertion'][] = ($assertion[$differentField] === null ? 'NULL' : $assertion[$differentField]);
490  $columns['record'][] = ($record[$differentField] === null ? 'NULL' : $record[$differentField]);
491  }
492 
493  foreach ($columns as $columnIndex => $column) {
494  $columnLength = null;
495  foreach ($column as $value) {
496  if (strpos($value, '<?xml') === 0) {
497  $value = '[see diff]';
498  }
499  $valueLength = strlen($value);
500  if (empty($columnLength) || $valueLength > $columnLength) {
501  $columnLength = $valueLength;
502  }
503  }
504  foreach ($column as $valueIndex => $value) {
505  if (strpos($value, '<?xml') === 0) {
506  if ($columnIndex === 'assertion') {
507  try {
508  $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$columns['fields'][$valueIndex]]);
509  } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
510  $linesFromXmlValues[] = 'Diff for field "' . $columns['fields'][$valueIndex] . '":' . PHP_EOL . $e->getComparisonFailure()->getDiff();
511  }
512  }
513  $value = '[see diff]';
514  }
515  $lines[$valueIndex][$columnIndex] = str_pad($value, $columnLength, ' ');
516  }
517  }
518 
519  foreach ($lines as $line) {
520  $result .= implode('|', $line) . PHP_EOL;
521  }
522 
523  foreach ($linesFromXmlValues as $lineFromXmlValues) {
524  $result .= PHP_EOL . $lineFromXmlValues . PHP_EOL;
525  }
526 
527  return $result;
528  }
529 
538  protected function getDifferentFields(array $assertion, array $record)
539  {
540  $differentFields = [];
541 
542  foreach ($assertion as $field => $value) {
543  if (strpos($value, '\\*') === 0) {
544  continue;
545  } elseif (strpos($value, '<?xml') === 0) {
546  try {
547  $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$field]);
548  } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
549  $differentFields[] = $field;
550  }
551  } elseif ($value === null && $record[$field] !== $value) {
552  $differentFields[] = $field;
553  } elseif ((string)$record[$field] !== (string)$value) {
554  $differentFields[] = $field;
555  }
556  }
557 
558  return $differentFields;
559  }
560 
565  protected function setUpFrontendRootPage($pageId, array $typoScriptFiles = [])
566  {
567  $pageId = (int)$pageId;
568  $page = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . $pageId);
569 
570  if (empty($page)) {
571  $this->fail('Cannot set up frontend root page "' . $pageId . '"');
572  }
573 
574  $pagesFields = [
575  'is_siteroot' => 1
576  ];
577 
578  $this->getDatabaseConnection()->exec_UPDATEquery('pages', 'uid=' . $pageId, $pagesFields);
579 
580  $templateFields = [
581  'pid' => $pageId,
582  'title' => '',
583  'config' => '',
584  'clear' => 3,
585  'root' => 1,
586  ];
587 
588  foreach ($typoScriptFiles as $typoScriptFile) {
589  $templateFields['config'] .= '<INCLUDE_TYPOSCRIPT: source="FILE:' . $typoScriptFile . '">' . LF;
590  }
591 
592  $this->getDatabaseConnection()->exec_DELETEquery('sys_template', 'pid = ' . $pageId);
593  $this->getDatabaseConnection()->exec_INSERTquery('sys_template', $templateFields);
594  }
595 
602  protected function addTypoScriptToTemplateRecord($pageId, $typoScript)
603  {
604  $connection = $this->getDatabaseConnection();
605 
606  $template = $connection->exec_SELECTgetSingleRow('*', 'sys_template', 'pid = '. $pageId . ' AND root = 1');
607  if (empty($template)) {
608  $this->fail('Cannot find root template on page with id: "' . $pageId . '"');
609  }
610  $updateFields['config'] = $template['config'] . LF . $typoScript;
611  $connection->exec_UPDATEquery(
612  'sys_template',
613  'uid = ' . $template['uid'],
614  $updateFields
615  );
616  }
617 
627  protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = true, $frontendUserId = 0)
628  {
629  $pageId = (int)$pageId;
630  $languageId = (int)$languageId;
631 
632  $additionalParameter = '';
633 
634  if (!empty($frontendUserId)) {
635  $additionalParameter .= '&frontendUserId=' . (int)$frontendUserId;
636  }
637  if (!empty($backendUserId)) {
638  $additionalParameter .= '&backendUserId=' . (int)$backendUserId;
639  }
640  if (!empty($workspaceId)) {
641  $additionalParameter .= '&workspaceId=' . (int)$workspaceId;
642  }
643 
644  $arguments = [
645  'documentRoot' => $this->getInstancePath(),
646  'requestUrl' => 'http://localhost/?id=' . $pageId . '&L=' . $languageId . $additionalParameter,
647  ];
648 
649  $template = new \Text_Template(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/request.tpl');
650  $template->setVar(
651  [
652  'arguments' => var_export($arguments, true),
653  'originalRoot' => ORIGINAL_ROOT,
654  ]
655  );
656 
657  $php = \PHPUnit_Util_PHP::factory();
658  $response = $php->runJob($template->render());
659  $result = json_decode($response['stdout'], true);
660 
661  if ($result === null) {
662  $this->fail('Frontend Response is empty');
663  }
664 
665  if ($failOnFailure && $result['status'] === Response::STATUS_Failure) {
666  $this->fail('Frontend Response has failure:' . LF . $result['error']);
667  }
668 
669  $response = new Response($result['status'], $result['content'], $result['error']);
670  return $response;
671  }
672 }
$database
Definition: server.php:40
getAllRecords($tableName, $hasUidField=false)
static read($fileName, $applyDefaultValues=false)
Definition: DataSet.php:34
getFrontendResponse($pageId, $languageId=0, $backendUserId=0, $workspaceId=0, $failOnFailure=true, $frontendUserId=0)
getDifferentFields(array $assertion, array $record)
assertInRecords(array $expectedRecord, array $actualRecords)
renderRecords(array $assertion, array $record)
setUpFrontendRootPage($pageId, array $typoScriptFiles=[])
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']