[TASK] Doctrine: Migrate functional test cases in ext:core
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Functional / DataHandling / AbstractDataHandlerActionTestCase.php
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;
18 use TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\DataSet;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Functional test for the DataHandler
23 */
24 abstract class AbstractDataHandlerActionTestCase extends \TYPO3\CMS\Core\Tests\FunctionalTestCase
25 {
26 const VALUE_BackendUserId = 1;
27
28 /**
29 * @var string
30 */
31 protected $scenarioDataSetDirectory;
32
33 /**
34 * @var string
35 */
36 protected $assertionDataSetDirectory;
37
38 /**
39 * If this value is NULL, log entries are not considered.
40 * If it's an integer value, the number of log entries is asserted.
41 *
42 * @var NULL|int
43 */
44 protected $expectedErrorLogEntries = 0;
45
46 /**
47 * @var array
48 */
49 protected $testExtensionsToLoad = array(
50 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial',
51 );
52
53 /**
54 * @var array
55 */
56 protected $pathsToLinkInTestInstance = array(
57 'typo3/sysext/core/Tests/Functional/Fixtures/Frontend/AdditionalConfiguration.php' => 'typo3conf/AdditionalConfiguration.php',
58 );
59
60 /**
61 * @var array
62 */
63 protected $recordIds = array();
64
65 /**
66 * @var \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService
67 */
68 protected $actionService;
69
70 /**
71 * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
72 */
73 protected $backendUser;
74
75 protected function setUp()
76 {
77 parent::setUp();
78
79 $this->backendUser = $this->setUpBackendUserFromFixture(self::VALUE_BackendUserId);
80 // By default make tests on live workspace
81 $this->backendUser->workspace = 0;
82
83 $this->actionService = $this->getActionService();
84 \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->initializeLanguageObject();
85 }
86
87 protected function tearDown()
88 {
89 $this->assertErrorLogEntries();
90 unset($this->actionService);
91 unset($this->recordIds);
92 parent::tearDown();
93 }
94
95 /**
96 * @return \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService
97 */
98 protected function getActionService()
99 {
100 return GeneralUtility::makeInstance(
101 \TYPO3\CMS\Core\Tests\Functional\DataHandling\Framework\ActionService::class
102 );
103 }
104
105 /**
106 * @param string $dataSetName
107 */
108 protected function importScenarioDataSet($dataSetName)
109 {
110 $fileName = rtrim($this->scenarioDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
111 $fileName = GeneralUtility::getFileAbsFileName($fileName);
112
113 $dataSet = DataSet::read($fileName, true);
114
115 foreach ($dataSet->getTableNames() as $tableName) {
116 foreach ($dataSet->getElements($tableName) as $element) {
117 $connection = $this->getConnectionPool()
118 ->getConnectionForTable($tableName);
119 try {
120 $connection->insert($tableName, $element);
121 } catch (DBALException $e) {
122 $this->fail('SQL Error for table "' . $tableName . '": ' . LF . $e->getMessage());
123 }
124 }
125 }
126 }
127
128 protected function assertAssertionDataSet($dataSetName)
129 {
130 $fileName = rtrim($this->assertionDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
131 $fileName = GeneralUtility::getFileAbsFileName($fileName);
132
133 $dataSet = DataSet::read($fileName);
134 $failMessages = array();
135
136 foreach ($dataSet->getTableNames() as $tableName) {
137 $hasUidField = ($dataSet->getIdIndex($tableName) !== null);
138 $records = $this->getAllRecords($tableName, $hasUidField);
139 foreach ($dataSet->getElements($tableName) as $assertion) {
140 $result = $this->assertInRecords($assertion, $records);
141 if ($result === false) {
142 if ($hasUidField && empty($records[$assertion['uid']])) {
143 $failMessages[] = 'Record "' . $tableName . ':' . $assertion['uid'] . '" not found in database';
144 continue;
145 }
146 $recordIdentifier = $tableName . ($hasUidField ? ':' . $assertion['uid'] : '');
147 $additionalInformation = ($hasUidField ? $this->renderRecords($assertion, $records[$assertion['uid']]) : $this->arrayToString($assertion));
148 $failMessages[] = 'Assertion in data-set failed for "' . $recordIdentifier . '":' . LF . $additionalInformation;
149 // Unset failed asserted record
150 if ($hasUidField) {
151 unset($records[$assertion['uid']]);
152 }
153 } else {
154 // Unset asserted record
155 unset($records[$result]);
156 // Increase assertion counter
157 $this->assertTrue($result !== false);
158 }
159 }
160 if (!empty($records)) {
161 foreach ($records as $record) {
162 $recordIdentifier = $tableName . ':' . $record['uid'];
163 $emptyAssertion = array_fill_keys($dataSet->getFields($tableName), '[none]');
164 $reducedRecord = array_intersect_key($record, $emptyAssertion);
165 $additionalInformation = ($hasUidField ? $this->renderRecords($emptyAssertion, $reducedRecord) : $this->arrayToString($reducedRecord));
166 $failMessages[] = 'Not asserted record found for "' . $recordIdentifier . '":' . LF . $additionalInformation;
167 }
168 }
169 }
170
171 if (!empty($failMessages)) {
172 $this->fail(implode(LF, $failMessages));
173 }
174 }
175
176 /**
177 * @param array $assertion
178 * @param array $records
179 * @return bool|int|string
180 */
181 protected function assertInRecords(array $assertion, array $records)
182 {
183 foreach ($records as $index => $record) {
184 $differentFields = $this->getDifferentFields($assertion, $record);
185
186 if (empty($differentFields)) {
187 return $index;
188 }
189 }
190
191 return false;
192 }
193
194 /**
195 * Asserts correct number of warning and error log entries.
196 *
197 * @return void
198 */
199 protected function assertErrorLogEntries()
200 {
201 if ($this->expectedErrorLogEntries === null) {
202 return;
203 }
204
205 $queryBuilder = $this->getConnectionPool()
206 ->getQueryBuilderForTable('sys_log');
207 $queryBuilder->getRestrictions()->removeAll();
208 $statement = $queryBuilder
209 ->select('*')
210 ->from('sys_log')
211 ->where($queryBuilder->expr()->in('error', [1, 2]))
212 ->execute();
213
214 $actualErrorLogEntries = $statement->rowCount();
215 if ($actualErrorLogEntries === $this->expectedErrorLogEntries) {
216 $this->assertSame($this->expectedErrorLogEntries, $actualErrorLogEntries);
217 } else {
218 $failureMessage = 'Expected ' . $this->expectedErrorLogEntries . ' entries in sys_log, but got ' . $actualErrorLogEntries . LF;
219 while ($entry = $statement->fetch()) {
220 $entryData = unserialize($entry['log_data']);
221 $entryMessage = vsprintf($entry['details'], $entryData);
222 $failureMessage .= '* ' . $entryMessage . LF;
223 }
224 $this->fail($failureMessage);
225 }
226 }
227
228 /**
229 * @param string $tableName
230 * @param bool $hasUidField
231 * @return array
232 */
233 protected function getAllRecords($tableName, $hasUidField = false)
234 {
235 $queryBuilder = $this->getConnectionPool()
236 ->getQueryBuilderForTable($tableName);
237 $queryBuilder->getRestrictions()->removeAll();
238 $statement = $queryBuilder
239 ->select('*')
240 ->from($tableName)
241 ->execute();
242
243 if (!$hasUidField) {
244 return $statement->fetchAll();
245 }
246
247 $allRecords = [];
248 while ($record = $statement->fetch()) {
249 $index = $record['uid'];
250 $allRecords[$index] = $record;
251 }
252
253 return $allRecords;
254 }
255
256 /**
257 * @param array $array
258 * @return string
259 */
260 protected function arrayToString(array $array)
261 {
262 $elements = array();
263 foreach ($array as $key => $value) {
264 if (is_array($value)) {
265 $value = $this->arrayToString($value);
266 }
267 $elements[] = "'" . $key . "' => '" . $value . "'";
268 }
269 return 'array(' . PHP_EOL . ' ' . implode(', ' . PHP_EOL . ' ', $elements) . PHP_EOL . ')' . PHP_EOL;
270 }
271
272 /**
273 * @param array $assertion
274 * @param array $record
275 * @return string
276 */
277 protected function renderRecords(array $assertion, array $record)
278 {
279 $differentFields = $this->getDifferentFields($assertion, $record);
280 $columns = array(
281 'fields' => array('Fields'),
282 'assertion' => array('Assertion'),
283 'record' => array('Record'),
284 );
285 $lines = array();
286 $linesFromXmlValues = array();
287 $result = '';
288
289 foreach ($differentFields as $differentField) {
290 $columns['fields'][] = $differentField;
291 $columns['assertion'][] = ($assertion[$differentField] === null ? 'NULL' : $assertion[$differentField]);
292 $columns['record'][] = ($record[$differentField] === null ? 'NULL' : $record[$differentField]);
293 }
294
295 foreach ($columns as $columnIndex => $column) {
296 $columnLength = null;
297 foreach ($column as $value) {
298 if (strpos($value, '<?xml') === 0) {
299 $value = '[see diff]';
300 }
301 $valueLength = strlen($value);
302 if (empty($columnLength) || $valueLength > $columnLength) {
303 $columnLength = $valueLength;
304 }
305 }
306 foreach ($column as $valueIndex => $value) {
307 if (strpos($value, '<?xml') === 0) {
308 if ($columnIndex === 'assertion') {
309 try {
310 $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$columns['fields'][$valueIndex]]);
311 } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
312 $linesFromXmlValues[] = 'Diff for field "' . $columns['fields'][$valueIndex] . '":' . PHP_EOL . $e->getComparisonFailure()->getDiff();
313 }
314 }
315 $value = '[see diff]';
316 }
317 $lines[$valueIndex][$columnIndex] = str_pad($value, $columnLength, ' ');
318 }
319 }
320
321 foreach ($lines as $line) {
322 $result .= implode('|', $line) . PHP_EOL;
323 }
324
325 foreach ($linesFromXmlValues as $lineFromXmlValues) {
326 $result .= PHP_EOL . $lineFromXmlValues . PHP_EOL;
327 }
328
329 return $result;
330 }
331
332 /**
333 * @param array $assertion
334 * @param array $record
335 * @return array
336 */
337 protected function getDifferentFields(array $assertion, array $record)
338 {
339 $differentFields = array();
340
341 foreach ($assertion as $field => $value) {
342 if (strpos($value, '\\*') === 0) {
343 continue;
344 } elseif (strpos($value, '<?xml') === 0) {
345 try {
346 $this->assertXmlStringEqualsXmlString((string)$value, (string)$record[$field]);
347 } catch (\PHPUnit_Framework_ExpectationFailedException $e) {
348 $differentFields[] = $field;
349 }
350 } elseif ($value === null && $record[$field] !== $value) {
351 $differentFields[] = $field;
352 } elseif ((string)$record[$field] !== (string)$value) {
353 $differentFields[] = $field;
354 }
355 }
356
357 return $differentFields;
358 }
359
360 /**
361 * @return \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\HasRecordConstraint
362 */
363 protected function getRequestSectionHasRecordConstraint()
364 {
365 return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\HasRecordConstraint();
366 }
367
368 /**
369 * @return \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\DoesNotHaveRecordConstraint
370 */
371 protected function getRequestSectionDoesNotHaveRecordConstraint()
372 {
373 return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\DoesNotHaveRecordConstraint();
374 }
375
376 /**
377 * @return \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureHasRecordConstraint
378 */
379 protected function getRequestSectionStructureHasRecordConstraint()
380 {
381 return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureHasRecordConstraint();
382 }
383
384 /**
385 * @return \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureDoesNotHaveRecordConstraint
386 */
387 protected function getRequestSectionStructureDoesNotHaveRecordConstraint()
388 {
389 return new \TYPO3\CMS\Core\Tests\Functional\Framework\Constraint\RequestSection\StructureDoesNotHaveRecordConstraint();
390 }
391 }