TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
AbstractDataHandlerActionTestCase.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Functional\DataHandling;
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 
17 use Doctrine\DBAL\DBALException;
21 
26 {
28 
33 
38 
45  protected $expectedErrorLogEntries = 0;
46 
50  protected $testExtensionsToLoad = [
51  'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial',
52  ];
53 
58  'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/AdditionalConfiguration.php' => 'typo3conf/AdditionalConfiguration.php',
59  ];
60 
64  protected $recordIds = [];
65 
69  protected $actionService;
70 
74  protected $backendUser;
75 
76  protected function setUp()
77  {
78  parent::setUp();
79 
80  $this->backendUser = $this->setUpBackendUserFromFixture(self::VALUE_BackendUserId);
81  // By default make tests on live workspace
82  $this->backendUser->workspace = 0;
83 
84  $this->actionService = $this->getActionService();
85  \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeLanguageObject();
86  }
87 
88  protected function tearDown()
89  {
90  $this->assertErrorLogEntries();
91  unset($this->actionService);
92  unset($this->recordIds);
93  parent::tearDown();
94  }
95 
99  protected function getActionService()
100  {
102  \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService::class
103  );
104  }
105 
109  protected function importScenarioDataSet($dataSetName)
110  {
111  $fileName = rtrim($this->scenarioDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
112  $fileName = GeneralUtility::getFileAbsFileName($fileName);
113 
114  $dataSet = DataSet::read($fileName, true);
115 
116  foreach ($dataSet->getTableNames() as $tableName) {
117  foreach ($dataSet->getElements($tableName) as $element) {
118  $connection = $this->getConnectionPool()
119  ->getConnectionForTable($tableName);
120  try {
121  $connection->insert($tableName, $element);
122  } catch (DBALException $e) {
123  $this->fail('SQL Error for table "' . $tableName . '": ' . LF . $e->getMessage());
124  }
125  }
126  }
127  }
128 
129  protected function assertAssertionDataSet($dataSetName)
130  {
131  $fileName = rtrim($this->assertionDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
132  $fileName = GeneralUtility::getFileAbsFileName($fileName);
133 
134  $dataSet = DataSet::read($fileName);
135  $failMessages = [];
136 
137  foreach ($dataSet->getTableNames() as $tableName) {
138  $hasUidField = ($dataSet->getIdIndex($tableName) !== null);
139  $records = $this->getAllRecords($tableName, $hasUidField);
140  foreach ($dataSet->getElements($tableName) as $assertion) {
141  $result = $this->assertInRecords($assertion, $records);
142  if ($result === false) {
143  if ($hasUidField && empty($records[$assertion['uid']])) {
144  $failMessages[] = 'Record "' . $tableName . ':' . $assertion['uid'] . '" not found in database';
145  continue;
146  }
147  $recordIdentifier = $tableName . ($hasUidField ? ':' . $assertion['uid'] : '');
148  $additionalInformation = ($hasUidField ? $this->renderRecords($assertion, $records[$assertion['uid']]) : $this->arrayToString($assertion));
149  $failMessages[] = 'Assertion in data-set failed for "' . $recordIdentifier . '":' . LF . $additionalInformation;
150  // Unset failed asserted record
151  if ($hasUidField) {
152  unset($records[$assertion['uid']]);
153  }
154  } else {
155  // Unset asserted record
156  unset($records[$result]);
157  // Increase assertion counter
158  $this->assertTrue($result !== false);
159  }
160  }
161  if (!empty($records)) {
162  foreach ($records as $record) {
163  $recordIdentifier = $tableName . ':' . $record['uid'];
164  $emptyAssertion = array_fill_keys($dataSet->getFields($tableName), '[none]');
165  $reducedRecord = array_intersect_key($record, $emptyAssertion);
166  $additionalInformation = ($hasUidField ? $this->renderRecords($emptyAssertion, $reducedRecord) : $this->arrayToString($reducedRecord));
167  $failMessages[] = 'Not asserted record found for "' . $recordIdentifier . '":' . LF . $additionalInformation;
168  }
169  }
170  }
171 
172  if (!empty($failMessages)) {
173  $this->fail(implode(LF, $failMessages));
174  }
175  }
176 
182  protected function assertInRecords(array $assertion, array $records)
183  {
184  foreach ($records as $index => $record) {
185  $differentFields = $this->getDifferentFields($assertion, $record);
186 
187  if (empty($differentFields)) {
188  return $index;
189  }
190  }
191 
192  return false;
193  }
194 
200  protected function assertErrorLogEntries()
201  {
202  if ($this->expectedErrorLogEntries === null) {
203  return;
204  }
205 
206  $queryBuilder = $this->getConnectionPool()
207  ->getQueryBuilderForTable('sys_log');
208  $queryBuilder->getRestrictions()->removeAll();
209  $statement = $queryBuilder
210  ->select('*')
211  ->from('sys_log')
212  ->where(
213  $queryBuilder->expr()->in(
214  'error',
215  $queryBuilder->createNamedParameter([1, 2], Connection::PARAM_INT_ARRAY)
216  )
217  )
218  ->execute();
219 
220  $actualErrorLogEntries = $statement->rowCount();
221  if ($actualErrorLogEntries === $this->expectedErrorLogEntries) {
222  $this->assertSame($this->expectedErrorLogEntries, $actualErrorLogEntries);
223  } else {
224  $failureMessage = 'Expected ' . $this->expectedErrorLogEntries . ' entries in sys_log, but got ' . $actualErrorLogEntries . LF;
225  while ($entry = $statement->fetch()) {
226  $entryData = unserialize($entry['log_data']);
227  $entryMessage = vsprintf($entry['details'], $entryData);
228  $failureMessage .= '* ' . $entryMessage . LF;
229  }
230  $this->fail($failureMessage);
231  }
232  }
233 
239  protected function getAllRecords($tableName, $hasUidField = false)
240  {
241  $queryBuilder = $this->getConnectionPool()
242  ->getQueryBuilderForTable($tableName);
243  $queryBuilder->getRestrictions()->removeAll();
244  $statement = $queryBuilder
245  ->select('*')
246  ->from($tableName)
247  ->execute();
248 
249  if (!$hasUidField) {
250  return $statement->fetchAll();
251  }
252 
253  $allRecords = [];
254  while ($record = $statement->fetch()) {
255  $index = $record['uid'];
256  $allRecords[$index] = $record;
257  }
258 
259  return $allRecords;
260  }
261 
266  protected function arrayToString(array $array)
267  {
268  $elements = [];
269  foreach ($array as $key => $value) {
270  if (is_array($value)) {
271  $value = $this->arrayToString($value);
272  }
273  $elements[] = "'" . $key . "' => '" . $value . "'";
274  }
275  return 'array(' . PHP_EOL . ' ' . implode(', ' . PHP_EOL . ' ', $elements) . PHP_EOL . ')' . PHP_EOL;
276  }
277 
283  protected function renderRecords(array $assertion, array $record)
284  {
285  $differentFields = $this->getDifferentFields($assertion, $record);
286  $columns = [
287  'fields' => ['Fields'],
288  'assertion' => ['Assertion'],
289  'record' => ['Record'],
290  ];
291  $lines = [];
292  $linesFromXmlValues = [];
293  $result = '';
294 
295  foreach ($differentFields as $differentField) {
296  $columns['fields'][] = $differentField;
297  $columns['assertion'][] = ($assertion[$differentField] === null ? 'NULL' : $assertion[$differentField]);
298  $columns['record'][] = ($record[$differentField] === null ? 'NULL' : $record[$differentField]);
299  }
300 
301  foreach ($columns as $columnIndex => $column) {
302  $columnLength = null;
303  foreach ($column as $value) {
304  if (strpos($value, '<?xml') === 0) {
305  $value = '[see diff]';
306  }
307  $valueLength = strlen($value);
308  if (empty($columnLength) || $valueLength > $columnLength) {
309  $columnLength = $valueLength;
310  }
311  }
312  foreach ($column as $valueIndex => $value) {
313  if (strpos($value, '<?xml') === 0) {
314  if ($columnIndex === 'assertion') {
315  try {
316  $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$columns['fields'][$valueIndex]]);
317  } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
318  $linesFromXmlValues[] = 'Diff for field "' . $columns['fields'][$valueIndex] . '":' . PHP_EOL . $e->getComparisonFailure()->getDiff();
319  }
320  }
321  $value = '[see diff]';
322  }
323  $lines[$valueIndex][$columnIndex] = str_pad($value, $columnLength, ' ');
324  }
325  }
326 
327  foreach ($lines as $line) {
328  $result .= implode('|', $line) . PHP_EOL;
329  }
330 
331  foreach ($linesFromXmlValues as $lineFromXmlValues) {
332  $result .= PHP_EOL . $lineFromXmlValues . PHP_EOL;
333  }
334 
335  return $result;
336  }
337 
343  protected function getDifferentFields(array $assertion, array $record)
344  {
345  $differentFields = [];
346 
347  foreach ($assertion as $field => $value) {
348  if (strpos($value, '\\*') === 0) {
349  continue;
350  } elseif (strpos($value, '<?xml') === 0) {
351  try {
352  $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$field]);
353  } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
354  $differentFields[] = $field;
355  }
356  } elseif ($value === null && $record[$field] !== $value) {
357  $differentFields[] = $field;
358  } elseif ((string)$record[$field] !== (string)$value) {
359  $differentFields[] = $field;
360  }
361  }
362 
363  return $differentFields;
364  }
365 
370  {
371  return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\HasRecordConstraint();
372  }
373 
378  {
379  return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\DoesNotHaveRecordConstraint();
380  }
381 
386  {
387  return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureHasRecordConstraint();
388  }
389 
394  {
395  return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureDoesNotHaveRecordConstraint();
396  }
397 }
static read($fileName, $applyDefaultValues=false)
Definition: DataSet.php:34
static makeInstance($className,...$constructorArguments)
static getFileAbsFileName($filename, $_=null, $_2=null)