1 | <?php |
||||||
2 | |||||||
3 | declare(strict_types=1); |
||||||
4 | |||||||
5 | namespace EdmondsCommerce\DoctrineStaticMeta\Tests\Large\A; |
||||||
6 | |||||||
7 | use Doctrine\Common\Inflector\Inflector; |
||||||
8 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\GenerateFieldCommand; |
||||||
9 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator; |
||||||
10 | use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator; |
||||||
11 | use EdmondsCommerce\DoctrineStaticMeta\Config; |
||||||
12 | use EdmondsCommerce\DoctrineStaticMeta\ConfigInterface; |
||||||
13 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Financial\MoneyEmbeddable; |
||||||
14 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Geo\AddressEmbeddable; |
||||||
15 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\Identity\FullNameEmbeddable; |
||||||
16 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Financial\HasMoneyEmbeddableTrait; |
||||||
17 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Geo\HasAddressEmbeddableTrait; |
||||||
18 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Traits\Identity\HasFullNameEmbeddableTrait; |
||||||
19 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\BusinessIdentifierCodeFieldTrait; |
||||||
20 | use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\String\NullableStringFieldTrait; |
||||||
21 | use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException; |
||||||
22 | use EdmondsCommerce\DoctrineStaticMeta\MappingHelper; |
||||||
23 | use EdmondsCommerce\DoctrineStaticMeta\Tests\Assets\AbstractLargeTest; |
||||||
24 | use EdmondsCommerce\DoctrineStaticMeta\Tests\Assets\AbstractTest; |
||||||
25 | use Exception; |
||||||
26 | use Psr\Container\ContainerExceptionInterface; |
||||||
27 | use Psr\Container\NotFoundExceptionInterface; |
||||||
28 | use RuntimeException; |
||||||
29 | |||||||
30 | use function array_diff; |
||||||
31 | use function array_keys; |
||||||
32 | use function ksort; |
||||||
33 | |||||||
34 | /** |
||||||
35 | * Class GeneratedCodeTest |
||||||
36 | * |
||||||
37 | * @package EdmondsCommerce\DoctrineStaticMeta\GeneratedCode |
||||||
38 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
||||||
39 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
||||||
40 | * @coversNothing |
||||||
41 | */ |
||||||
42 | class FullProjectBuildLargeTest extends AbstractLargeTest |
||||||
43 | { |
||||||
44 | public const TEST_ENTITY_NAMESPACE_BASE = self::TEST_PROJECT_ROOT_NAMESPACE |
||||||
45 | . '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME; |
||||||
46 | |||||||
47 | public const TEST_FIELD_TRAIT_NAMESPACE = self::TEST_FIELD_NAMESPACE_BASE . '\\Traits\\'; |
||||||
48 | |||||||
49 | public const TEST_ENTITY_PERSON = self::TEST_ENTITY_NAMESPACE_BASE . '\\Person'; |
||||||
50 | public const TEST_ENTITY_ADDRESS = self::TEST_ENTITY_NAMESPACE_BASE . '\\Attributes\\Address'; |
||||||
51 | public const TEST_ENTITY_EMAIL = self::TEST_ENTITY_NAMESPACE_BASE . '\\Attributes\\Email'; |
||||||
52 | public const TEST_ENTITY_COMPANY = self::TEST_ENTITY_NAMESPACE_BASE . '\\Company'; |
||||||
53 | public const TEST_ENTITY_DIRECTOR = self::TEST_ENTITY_NAMESPACE_BASE . '\\Company\\Director'; |
||||||
54 | public const TEST_ENTITY_ORDER = self::TEST_ENTITY_NAMESPACE_BASE . '\\Order'; |
||||||
55 | public const TEST_ENTITY_ORDER_ADDRESS = self::TEST_ENTITY_NAMESPACE_BASE . '\\Order\\Address'; |
||||||
56 | public const TEST_ENTITY_NAME_SPACING_SOME_CLIENT = self::TEST_ENTITY_NAMESPACE_BASE . '\\Some\\Client'; |
||||||
57 | public const TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT = self::TEST_ENTITY_NAMESPACE_BASE |
||||||
58 | . '\\Another\\Deeply\\Nested\\Client'; |
||||||
59 | public const TEST_ENTITY_PRODUCT = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product'; |
||||||
60 | public const TEST_ENTITY_PRODUCT_STOCK = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product\\Stock'; |
||||||
61 | public const TEST_ENTITY_PRODUCT_REVIEW = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product\\Review'; |
||||||
62 | public const TEST_ENTITY_PRODUCT_DATA = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product\\Data'; |
||||||
63 | public const TEST_ENTITY_PRODUCT_DATA_ITEM = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product\\Data\\Item'; |
||||||
64 | public const TEST_ENTITY_PRODUCT_DATA_ITEM_THING = self::TEST_ENTITY_NAMESPACE_BASE . |
||||||
65 | '\\Product\\Data\\Item\\Thing'; |
||||||
66 | public const TEST_ENTITY_PRODUCT_DATA_ITEM_FOO = self::TEST_ENTITY_NAMESPACE_BASE . |
||||||
67 | '\\Product\\Data\\Item\\Foo'; |
||||||
68 | public const TEST_ENTITY_PRODUCT_EXTRA = self::TEST_ENTITY_NAMESPACE_BASE . '\\Product\\Extra'; |
||||||
69 | |||||||
70 | public const TEST_ENTITIES = [ |
||||||
71 | self::TEST_ENTITY_PERSON, |
||||||
72 | self::TEST_ENTITY_ADDRESS, |
||||||
73 | self::TEST_ENTITY_EMAIL, |
||||||
74 | self::TEST_ENTITY_COMPANY, |
||||||
75 | self::TEST_ENTITY_DIRECTOR, |
||||||
76 | self::TEST_ENTITY_ORDER, |
||||||
77 | self::TEST_ENTITY_ORDER_ADDRESS, |
||||||
78 | self::TEST_ENTITY_NAME_SPACING_SOME_CLIENT, |
||||||
79 | self::TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT, |
||||||
80 | self::TEST_ENTITY_PRODUCT, |
||||||
81 | self::TEST_ENTITY_PRODUCT_STOCK, |
||||||
82 | self::TEST_ENTITY_PRODUCT_REVIEW, |
||||||
83 | self::TEST_ENTITY_PRODUCT_DATA, |
||||||
84 | self::TEST_ENTITY_PRODUCT_DATA_ITEM, |
||||||
85 | self::TEST_ENTITY_PRODUCT_DATA_ITEM_THING, |
||||||
86 | self::TEST_ENTITY_PRODUCT_DATA_ITEM_FOO, |
||||||
87 | self::TEST_ENTITY_PRODUCT_EXTRA, |
||||||
88 | ]; |
||||||
89 | |||||||
90 | public const TEST_RELATIONS = [ |
||||||
91 | [ |
||||||
92 | self::TEST_ENTITY_PERSON, |
||||||
93 | RelationsGenerator::HAS_UNIDIRECTIONAL_MANY_TO_ONE, |
||||||
94 | self::TEST_ENTITY_ADDRESS, |
||||||
95 | true, |
||||||
96 | ], |
||||||
97 | [ |
||||||
98 | self::TEST_ENTITY_PERSON, |
||||||
99 | RelationsGenerator::HAS_ONE_TO_MANY, |
||||||
100 | self::TEST_ENTITY_EMAIL, |
||||||
101 | true, |
||||||
102 | ], |
||||||
103 | [ |
||||||
104 | self::TEST_ENTITY_COMPANY, |
||||||
105 | RelationsGenerator::HAS_MANY_TO_MANY, |
||||||
106 | self::TEST_ENTITY_DIRECTOR, |
||||||
107 | false, |
||||||
108 | ], |
||||||
109 | [ |
||||||
110 | self::TEST_ENTITY_COMPANY, |
||||||
111 | RelationsGenerator::HAS_ONE_TO_MANY, |
||||||
112 | self::TEST_ENTITY_ADDRESS, |
||||||
113 | false, |
||||||
114 | ], |
||||||
115 | [ |
||||||
116 | self::TEST_ENTITY_COMPANY, |
||||||
117 | RelationsGenerator::HAS_UNIDIRECTIONAL_ONE_TO_MANY, |
||||||
118 | self::TEST_ENTITY_EMAIL, |
||||||
119 | true, |
||||||
120 | ], |
||||||
121 | [ |
||||||
122 | self::TEST_ENTITY_DIRECTOR, |
||||||
123 | RelationsGenerator::HAS_ONE_TO_ONE, |
||||||
124 | self::TEST_ENTITY_PERSON, |
||||||
125 | false, |
||||||
126 | ], |
||||||
127 | [ |
||||||
128 | self::TEST_ENTITY_ORDER, |
||||||
129 | RelationsGenerator::HAS_MANY_TO_ONE, |
||||||
130 | self::TEST_ENTITY_PERSON, |
||||||
131 | true, |
||||||
132 | ], |
||||||
133 | [ |
||||||
134 | self::TEST_ENTITY_ORDER, |
||||||
135 | RelationsGenerator::HAS_ONE_TO_MANY, |
||||||
136 | self::TEST_ENTITY_ORDER_ADDRESS, |
||||||
137 | false, |
||||||
138 | ], |
||||||
139 | [ |
||||||
140 | self::TEST_ENTITY_ORDER_ADDRESS, |
||||||
141 | RelationsGenerator::HAS_UNIDIRECTIONAL_ONE_TO_ONE, |
||||||
142 | self::TEST_ENTITY_ADDRESS, |
||||||
143 | true, |
||||||
144 | ], |
||||||
145 | [ |
||||||
146 | self::TEST_ENTITY_COMPANY, |
||||||
147 | RelationsGenerator::HAS_ONE_TO_ONE, |
||||||
148 | self::TEST_ENTITY_NAME_SPACING_SOME_CLIENT, |
||||||
149 | false, |
||||||
150 | ], |
||||||
151 | [ |
||||||
152 | self::TEST_ENTITY_COMPANY, |
||||||
153 | RelationsGenerator::HAS_ONE_TO_ONE, |
||||||
154 | self::TEST_ENTITY_NAME_SPACING_ANOTHER_CLIENT, |
||||||
155 | true, |
||||||
156 | ], |
||||||
157 | [ |
||||||
158 | self::TEST_ENTITY_PRODUCT, |
||||||
159 | RelationsGenerator::HAS_REQUIRED_MANY_TO_MANY, |
||||||
160 | self::TEST_ENTITY_PRODUCT_STOCK, |
||||||
161 | true, |
||||||
162 | ], |
||||||
163 | [ |
||||||
164 | self::TEST_ENTITY_PRODUCT_REVIEW, |
||||||
165 | RelationsGenerator::HAS_REQUIRED_MANY_TO_ONE, |
||||||
166 | self::TEST_ENTITY_PRODUCT, |
||||||
167 | false, |
||||||
168 | ], |
||||||
169 | [ |
||||||
170 | self::TEST_ENTITY_PRODUCT, |
||||||
171 | RelationsGenerator::HAS_REQUIRED_ONE_TO_ONE, |
||||||
172 | self::TEST_ENTITY_PRODUCT_EXTRA, |
||||||
173 | true, |
||||||
174 | ], |
||||||
175 | [ |
||||||
176 | self::TEST_ENTITY_PRODUCT, |
||||||
177 | RelationsGenerator::HAS_ONE_TO_ONE, |
||||||
178 | self::TEST_ENTITY_PRODUCT_DATA, |
||||||
179 | true, |
||||||
180 | ], |
||||||
181 | [ |
||||||
182 | self::TEST_ENTITY_PRODUCT_DATA_ITEM, |
||||||
183 | RelationsGenerator::HAS_REQUIRED_UNIDIRECTIONAL_MANY_TO_ONE, |
||||||
184 | self::TEST_ENTITY_PRODUCT_DATA, |
||||||
185 | false, |
||||||
186 | ], |
||||||
187 | [ |
||||||
188 | self::TEST_ENTITY_PRODUCT_DATA_ITEM_THING, |
||||||
189 | RelationsGenerator::HAS_REQUIRED_UNIDIRECTIONAL_ONE_TO_ONE, |
||||||
190 | self::TEST_ENTITY_PRODUCT_DATA, |
||||||
191 | false, |
||||||
192 | ], |
||||||
193 | [ |
||||||
194 | self::TEST_ENTITY_PRODUCT_DATA_ITEM_FOO, |
||||||
195 | RelationsGenerator::HAS_REQUIRED_UNIDIRECTIONAL_ONE_TO_MANY, |
||||||
196 | self::TEST_ENTITY_PRODUCT_DATA, |
||||||
197 | false, |
||||||
198 | ], |
||||||
199 | [ |
||||||
200 | self::TEST_ENTITY_PRODUCT_DATA_ITEM_FOO, |
||||||
201 | RelationsGenerator::HAS_REQUIRED_ONE_TO_MANY, |
||||||
202 | self::TEST_ENTITY_PERSON, |
||||||
203 | false, |
||||||
204 | ], |
||||||
205 | ]; |
||||||
206 | |||||||
207 | public const TEST_FIELD_NAMESPACE_BASE = self::TEST_PROJECT_ROOT_NAMESPACE . '\\Entity\\Fields'; |
||||||
208 | |||||||
209 | public const UNIQUEABLE_FIELD_TYPES = [ |
||||||
210 | MappingHelper::TYPE_INTEGER, |
||||||
211 | MappingHelper::TYPE_STRING, |
||||||
212 | ]; |
||||||
213 | |||||||
214 | public const DUPLICATE_SHORT_NAME_FIELDS = [ |
||||||
215 | [self::TEST_FIELD_NAMESPACE_BASE . '\\Traits\\Something\\FooFieldTrait', NullableStringFieldTrait::class], |
||||||
216 | [ |
||||||
217 | self::TEST_FIELD_NAMESPACE_BASE . '\\Traits\\Otherthing\\FooFieldTrait', |
||||||
218 | BusinessIdentifierCodeFieldTrait::class, |
||||||
219 | ], |
||||||
220 | ]; |
||||||
221 | |||||||
222 | public const EMBEDDABLE_TRAIT_BASE = self::TEST_PROJECT_ROOT_NAMESPACE . '\\Entity\\Embeddable\\Traits'; |
||||||
223 | |||||||
224 | public const TEST_EMBEDDABLES = [ |
||||||
225 | [ |
||||||
226 | MoneyEmbeddable::class, |
||||||
227 | self::EMBEDDABLE_TRAIT_BASE . '\\Financial\\HasPriceEmbeddableTrait', |
||||||
228 | 'PriceEmbeddable', |
||||||
229 | ], |
||||||
230 | [ |
||||||
231 | AddressEmbeddable::class, |
||||||
232 | self::EMBEDDABLE_TRAIT_BASE . '\\Geo\\HasHeadOfficeEmbeddableTrait', |
||||||
233 | 'HeadOfficeEmbeddable', |
||||||
234 | ], |
||||||
235 | [ |
||||||
236 | FullNameEmbeddable::class, |
||||||
237 | self::EMBEDDABLE_TRAIT_BASE . '\\Identity\\HasPersonEmbeddableTrait', |
||||||
238 | 'PersonEmbeddable', |
||||||
239 | ], |
||||||
240 | ]; |
||||||
241 | public const BASH_PHPNOXDEBUG_FUNCTION = <<<'BASH' |
||||||
242 | function phpNoXdebug { |
||||||
243 | debugMode="off" |
||||||
244 | if [[ "$-" == *x* ]] |
||||||
245 | then |
||||||
246 | debugMode='on' |
||||||
247 | fi |
||||||
248 | if [[ "$debugMode" == "on" ]] |
||||||
249 | then |
||||||
250 | set +x |
||||||
251 | fi |
||||||
252 | local returnCode; |
||||||
253 | local temporaryPath="$(mktemp -t php.XXXX).ini" |
||||||
254 | # Using awk to ensure that files ending without newlines do not lead to configuration error |
||||||
255 | /usr/bin/php -i | grep "\.ini" | grep -o -e '\(/[a-z0-9._-]\+\)\+\.ini' | grep -v xdebug \ |
||||||
256 | | xargs awk 'FNR==1{print ""}1' > "$temporaryPath" |
||||||
257 | #Run PHP with temp config with no xdebug, display errors on stderr |
||||||
258 | set +e |
||||||
259 | /usr/bin/php -n -c "$temporaryPath" "$@" |
||||||
260 | returnCode=$? |
||||||
261 | set -e |
||||||
262 | rm -f "$temporaryPath" |
||||||
263 | if [[ "$debugMode" == "on" ]] |
||||||
264 | then |
||||||
265 | set -x |
||||||
266 | fi |
||||||
267 | return ${returnCode}; |
||||||
268 | } |
||||||
269 | |||||||
270 | BASH; |
||||||
271 | /** |
||||||
272 | * @var string |
||||||
273 | */ |
||||||
274 | private $workDir; |
||||||
275 | |||||||
276 | /** |
||||||
277 | * @throws Exception |
||||||
278 | * @throws ContainerExceptionInterface |
||||||
279 | * @throws NotFoundExceptionInterface |
||||||
280 | * @SuppressWarnings(PHPMD.Superglobals) |
||||||
281 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||||
282 | */ |
||||||
283 | public function setup() |
||||||
284 | { |
||||||
285 | if ($this->isQuickTests()) { |
||||||
286 | return; |
||||||
287 | } |
||||||
288 | $this->assertNoUncommitedChanges(); |
||||||
289 | $this->assertWeCheckAllPossibleRelationTypes(); |
||||||
290 | $this->workDir = $this->isTravis() ? |
||||||
291 | AbstractTest::VAR_PATH . '/GeneratedCodeTest' |
||||||
292 | : sys_get_temp_dir() . '/dsm/test-project'; |
||||||
293 | $this->entitiesPath = $this->workDir . '/src/Entities'; |
||||||
294 | $this->getFileSystem()->mkdir($this->workDir); |
||||||
295 | $this->emptyDirectory($this->workDir . ''); |
||||||
296 | $this->getFileSystem()->mkdir($this->entitiesPath); |
||||||
297 | $this->setupContainer($this->entitiesPath); |
||||||
298 | $this->initRebuildFile(); |
||||||
299 | $this->setupGeneratedDb(); |
||||||
300 | $this->initComposerAndInstall(); |
||||||
301 | $fileSystem = $this->getFileSystem(); |
||||||
302 | $fileSystem->mkdir( |
||||||
303 | [ |
||||||
304 | $this->workDir . '/tests/', |
||||||
305 | $this->workDir . '/cache/Proxies', |
||||||
306 | $this->workDir . '/cache/qa', |
||||||
307 | $this->workDir . '/qaConfig', |
||||||
308 | $this->workDir . '/var/qa/phpunit_logs/', |
||||||
309 | ] |
||||||
310 | ); |
||||||
311 | file_put_contents( |
||||||
312 | $this->workDir . '/qaConfig/phpunit.xml', |
||||||
313 | <<<XML |
||||||
314 | <phpunit |
||||||
315 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
316 | xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/3.7/phpunit.xsd" |
||||||
317 | cacheTokens="false" |
||||||
318 | colors="true" |
||||||
319 | verbose="true" |
||||||
320 | bootstrap="../tests/bootstrap.php" |
||||||
321 | printerClass="\EdmondsCommerce\PHPQA\PHPUnit\TestDox\CliTestDoxPrinter" |
||||||
322 | cacheResult="true" |
||||||
323 | cacheResultFile="../var/qa/.phpunit.result.cache" |
||||||
324 | executionOrder="depends,defects" |
||||||
325 | > |
||||||
326 | <testsuites> |
||||||
327 | <testsuite name="tests"> |
||||||
328 | <directory suffix="Test.php">../tests/</directory> |
||||||
329 | </testsuite> |
||||||
330 | </testsuites> |
||||||
331 | </phpunit> |
||||||
332 | XML |
||||||
333 | ); |
||||||
334 | $fileSystem->symlink($this->workDir . '/qaConfig/phpunit.xml', $this->workDir . '/phpunit.xml'); |
||||||
335 | |||||||
336 | $fileSystem->mirror( |
||||||
337 | __DIR__ . '/../../../qaConfig/codingStandard', |
||||||
338 | $this->workDir . '/qaConfig/codingStandard' |
||||||
339 | ); |
||||||
340 | $fileSystem->copy( |
||||||
341 | __DIR__ . '/../../../qaConfig/qaConfig.inc.bash', |
||||||
342 | $this->workDir . '/qaConfig/qaConfig.inc.bash' |
||||||
343 | ); |
||||||
344 | $fileSystem->copy(__DIR__ . '/../../../cli-config.php', $this->workDir . '/cli-config.php'); |
||||||
345 | file_put_contents($this->workDir . '/README.md', '#Generated Code'); |
||||||
346 | |||||||
347 | $entities = $this->generateEntities(); |
||||||
348 | $this->generateRelations(); |
||||||
349 | $this->generateFields(); |
||||||
350 | $this->setFields( |
||||||
351 | $entities, |
||||||
352 | $this->getFieldFqns() |
||||||
353 | ); |
||||||
354 | $this->setTheDuplicateNamedFields($entities); |
||||||
355 | foreach ($entities as $entityFqn) { |
||||||
356 | foreach ( |
||||||
357 | [ |
||||||
358 | HasMoneyEmbeddableTrait::class, |
||||||
359 | HasFullNameEmbeddableTrait::class, |
||||||
360 | HasAddressEmbeddableTrait::class, |
||||||
361 | ] as $embeddableTraitFqn |
||||||
362 | ) { |
||||||
363 | $this->setEmbeddable($entityFqn, $embeddableTraitFqn); |
||||||
364 | } |
||||||
365 | foreach (self::TEST_EMBEDDABLES as $embeddable) { |
||||||
366 | [$archetypeObject, $traitFqn, $className] = $embeddable; |
||||||
367 | $this->generateEmbeddable( |
||||||
368 | '\\' . $archetypeObject, |
||||||
369 | $className |
||||||
370 | ); |
||||||
371 | $this->setEmbeddable($entityFqn, $traitFqn); |
||||||
372 | } |
||||||
373 | } |
||||||
374 | $this->removeUnusedRelations(); |
||||||
375 | $this->execDoctrine('dsm:finalise:build'); |
||||||
376 | $this->execDoctrine('orm:clear-cache:metadata'); |
||||||
377 | $this->execDoctrine('orm:schema-tool:update --force'); |
||||||
378 | $this->execDoctrine('orm:validate-schema'); |
||||||
379 | } |
||||||
380 | |||||||
381 | /** |
||||||
382 | * We need to check for uncommited changes in the main project. If there are, then the generated code tests will |
||||||
383 | * not get them as it works by cloning this repo via the filesystem |
||||||
384 | */ |
||||||
385 | protected function assertNoUncommitedChanges(): void |
||||||
386 | { |
||||||
387 | if ($this->isTravis()) { |
||||||
388 | return; |
||||||
389 | } |
||||||
390 | exec("git status | grep -E 'nothing to commit, working .*? clean' ", $output, $exitCode); |
||||||
391 | if (0 !== $exitCode) { |
||||||
392 | $this->markTestSkipped( |
||||||
393 | 'uncommitted changes detected in this project, ' |
||||||
394 | . 'there is no point running the generated code test as it will not have your uncommitted changes.' |
||||||
395 | . "\n\n" . implode("\n", $output) |
||||||
396 | ); |
||||||
397 | } |
||||||
398 | } |
||||||
399 | |||||||
400 | protected function assertWeCheckAllPossibleRelationTypes(): void |
||||||
401 | { |
||||||
402 | $included = $toTest = []; |
||||||
403 | foreach (RelationsGenerator::HAS_TYPES as $hasType) { |
||||||
404 | if (false !== \ts\stringContains($hasType, RelationsGenerator::PREFIX_INVERSE)) { |
||||||
405 | continue; |
||||||
406 | } |
||||||
407 | $toTest[$hasType] = true; |
||||||
408 | } |
||||||
409 | ksort($toTest); |
||||||
410 | foreach (self::TEST_RELATIONS as $relation) { |
||||||
411 | $included[$relation[1]] = true; |
||||||
412 | } |
||||||
413 | ksort($included); |
||||||
414 | $missing = array_diff(array_keys($toTest), array_keys($included)); |
||||||
415 | self::assertEmpty( |
||||||
416 | $missing, |
||||||
417 | 'We are not testing all relation types - ' |
||||||
418 | . 'these ones have not been included: ' |
||||||
419 | . print_r($missing, true) |
||||||
420 | ); |
||||||
421 | } |
||||||
422 | |||||||
423 | protected function initRebuildFile(): void |
||||||
424 | { |
||||||
425 | $bash = |
||||||
426 | <<<'BASH' |
||||||
427 | #!/usr/bin/env bash |
||||||
428 | readonly DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; |
||||||
429 | cd "$DIR"; |
||||||
430 | set -e |
||||||
431 | set -u |
||||||
432 | set -o pipefail |
||||||
433 | standardIFS="$IFS" |
||||||
434 | IFS=$'\n\t' |
||||||
435 | echo " |
||||||
436 | =========================================== |
||||||
437 | $(hostname) $0 $@ |
||||||
438 | =========================================== |
||||||
439 | " |
||||||
440 | # Error Handling |
||||||
441 | backTraceExit () { |
||||||
442 | local err=$? |
||||||
443 | set +o xtrace |
||||||
444 | local code="${1:-1}" |
||||||
445 | printf "\n\nError in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}. '${BASH_COMMAND}'\n\n exited with status: \n\n$err\n\n" |
||||||
446 | # Print out the stack trace described by $function_stack |
||||||
447 | if [ ${#FUNCNAME[@]} -gt 2 ] |
||||||
448 | then |
||||||
449 | echo "Call tree:" |
||||||
450 | for ((i=1;i<${#FUNCNAME[@]}-1;i++)) |
||||||
451 | do |
||||||
452 | echo " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]} ${FUNCNAME[$i]}(...)" |
||||||
453 | done |
||||||
454 | fi |
||||||
455 | echo "Exiting with status ${code}" |
||||||
456 | exit "${code}" |
||||||
457 | } |
||||||
458 | trap 'backTraceExit' ERR |
||||||
459 | set -o errtrace |
||||||
460 | # Error Handling Ends |
||||||
461 | |||||||
462 | echo "clearing out generated code and cache" |
||||||
463 | rm -rf src/* tests/* cache/* |
||||||
464 | |||||||
465 | echo "preparing empty Entities directory" |
||||||
466 | mkdir src/Entities |
||||||
467 | |||||||
468 | echo "making sure we have the latest version of code" |
||||||
469 | (cd vendor/edmondscommerce/doctrine-static-meta && git pull) |
||||||
470 | |||||||
471 | BASH; |
||||||
472 | if (!$this->isTravis()) { |
||||||
473 | $bash .= self::BASH_PHPNOXDEBUG_FUNCTION; |
||||||
474 | } |
||||||
475 | file_put_contents( |
||||||
476 | $this->workDir . '/rebuild.bash', |
||||||
477 | "\n\n" . $bash |
||||||
478 | ); |
||||||
479 | } |
||||||
480 | |||||||
481 | /** |
||||||
482 | * @return string Generated Database Name |
||||||
483 | * @throws Exception |
||||||
484 | * @throws ContainerExceptionInterface |
||||||
485 | * @throws NotFoundExceptionInterface |
||||||
486 | */ |
||||||
487 | protected function setupGeneratedDb(): string |
||||||
488 | { |
||||||
489 | $dbHost = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_HOST); |
||||||
490 | $dbUser = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_USER); |
||||||
491 | $dbPass = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_PASS); |
||||||
492 | $dbName = $this->container->get(Config::class)->get(ConfigInterface::PARAM_DB_NAME); |
||||||
493 | $link = mysqli_connect($dbHost, $dbUser, $dbPass); |
||||||
494 | if (!$link) { |
||||||
495 | throw new DoctrineStaticMetaException('Failed getting connection in ' . __METHOD__); |
||||||
496 | } |
||||||
497 | $generatedDbName = $dbName . '_generated'; |
||||||
498 | mysqli_query($link, "DROP DATABASE IF EXISTS $generatedDbName"); |
||||||
499 | mysqli_query( |
||||||
500 | $link, |
||||||
501 | "CREATE DATABASE $generatedDbName |
||||||
502 | CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci" |
||||||
503 | ); |
||||||
504 | mysqli_close($link); |
||||||
505 | |||||||
506 | $rebuildBash = <<<BASH |
||||||
507 | echo "Dropping and creating the DB $generatedDbName" |
||||||
508 | mysql -u $dbUser -p$dbPass -h $dbHost -e "DROP DATABASE IF EXISTS $generatedDbName"; |
||||||
509 | mysql -u $dbUser -p$dbPass -h $dbHost -e "CREATE DATABASE $generatedDbName |
||||||
510 | CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci"; |
||||||
511 | BASH; |
||||||
512 | $this->addToRebuildFile($rebuildBash); |
||||||
513 | file_put_contents( |
||||||
514 | $this->workDir . '/.env', |
||||||
515 | <<<EOF |
||||||
516 | export dbUser="{$dbUser}" |
||||||
517 | export dbPass="{$dbPass}" |
||||||
518 | export dbHost="{$dbHost}" |
||||||
519 | export dbName="$generatedDbName" |
||||||
520 | export devMode=0 |
||||||
521 | export dbDebug=0 |
||||||
522 | EOF |
||||||
523 | ); |
||||||
524 | |||||||
525 | return $generatedDbName; |
||||||
526 | } |
||||||
527 | |||||||
528 | /** |
||||||
529 | * @param string $bash |
||||||
530 | * |
||||||
531 | * @return bool |
||||||
532 | * @throws Exception |
||||||
533 | */ |
||||||
534 | protected function addToRebuildFile(string $bash): bool |
||||||
535 | { |
||||||
536 | $result = file_put_contents( |
||||||
537 | $this->workDir . '/rebuild.bash', |
||||||
538 | "\n\n" . $bash . "\n\n", |
||||||
539 | FILE_APPEND |
||||||
540 | ); |
||||||
541 | if (!$result) { |
||||||
542 | throw new RuntimeException('Failed writing to rebuild file'); |
||||||
543 | } |
||||||
544 | |||||||
545 | return true; |
||||||
546 | } |
||||||
547 | |||||||
548 | protected function initComposerAndInstall(): void |
||||||
549 | { |
||||||
550 | $relativePath = __DIR__ . '/../../../../doctrine-static-meta/'; |
||||||
551 | $vcsPath = realpath($relativePath); |
||||||
552 | if (false === $vcsPath) { |
||||||
553 | throw new RuntimeException('Failed getting realpath to main project at ' . $relativePath); |
||||||
554 | } |
||||||
555 | $namespace = str_replace('\\', '\\\\', self::TEST_PROJECT_ROOT_NAMESPACE); |
||||||
556 | $composerJson = <<<JSON |
||||||
557 | { |
||||||
558 | "require": { |
||||||
559 | "edmondscommerce/doctrine-static-meta": "dev-%s", |
||||||
560 | "php": ">=7.2" |
||||||
561 | }, |
||||||
562 | "repositories": [ |
||||||
563 | { |
||||||
564 | "type": "vcs", |
||||||
565 | "url": "%s" |
||||||
566 | }, |
||||||
567 | { |
||||||
568 | "type": "vcs", |
||||||
569 | "url": "https://github.com/edmondscommerce/Faker.git" |
||||||
570 | } |
||||||
571 | ], |
||||||
572 | "minimum-stability": "stable", |
||||||
573 | "require-dev": { |
||||||
574 | "fzaninotto/faker": "dev-dsm-patches@dev", |
||||||
575 | "edmondscommerce/phpqa": "~1", |
||||||
576 | "gossi/php-code-generator": "^0.5.0" |
||||||
577 | }, |
||||||
578 | "autoload": { |
||||||
579 | "psr-4": { |
||||||
580 | "$namespace\\\\": [ |
||||||
581 | "src/" |
||||||
582 | ] |
||||||
583 | } |
||||||
584 | }, |
||||||
585 | "autoload-dev": { |
||||||
586 | "psr-4": { |
||||||
587 | "$namespace\\\\": [ |
||||||
588 | "tests/" |
||||||
589 | ] |
||||||
590 | } |
||||||
591 | }, |
||||||
592 | "config": { |
||||||
593 | "bin-dir": "bin", |
||||||
594 | "preferred-install": { |
||||||
595 | "edmondscommerce/*": "source", |
||||||
596 | "fzaninotto/faker": "source", |
||||||
597 | "*": "dist" |
||||||
598 | }, |
||||||
599 | "optimize-autoloader": true |
||||||
600 | } |
||||||
601 | } |
||||||
602 | JSON; |
||||||
603 | |||||||
604 | $gitCurrentBranchName = trim(shell_exec("git branch | grep '*' | cut -d ' ' -f 2-")); |
||||||
605 | echo __LINE__ . ": \$gitCurrentBranchName $gitCurrentBranchName"; |
||||||
606 | if (\ts\stringContains($gitCurrentBranchName, 'HEAD detached at')) { |
||||||
607 | $gitCurrentBranchName = trim(str_replace('HEAD detached at', '', $gitCurrentBranchName), " \t\n\r\0\x0B()"); |
||||||
608 | echo __LINE__ . ": \$gitCurrentBranchName $gitCurrentBranchName"; |
||||||
609 | } elseif (\ts\stringContains($gitCurrentBranchName, 'detached from')) { |
||||||
610 | $gitCurrentBranchName = trim(str_replace('detached from', '', $gitCurrentBranchName), " \t\n\r\0\x0B()"); |
||||||
611 | echo __LINE__ . ": \$gitCurrentBranchName $gitCurrentBranchName"; |
||||||
612 | } |
||||||
613 | file_put_contents( |
||||||
614 | $this->workDir . '/composer.json', |
||||||
615 | sprintf($composerJson, $gitCurrentBranchName, $vcsPath) |
||||||
616 | ); |
||||||
617 | |||||||
618 | $phpCmd = $this->isTravis() ? 'php' : 'phpNoXdebug'; |
||||||
619 | $bashCmds = <<<BASH |
||||||
620 | |||||||
621 | $phpCmd $(which composer) install \ |
||||||
622 | --prefer-dist |
||||||
623 | |||||||
624 | $phpCmd $(which composer) dump-autoload --optimize |
||||||
625 | |||||||
626 | BASH; |
||||||
627 | $this->execBash($bashCmds); |
||||||
628 | } |
||||||
629 | |||||||
630 | /** |
||||||
631 | * Runs bash with strict error handling and verbose logging |
||||||
632 | * |
||||||
633 | * Will ensure the phpNoXdebugFunction is available and will CD into the correct directory before running commands |
||||||
634 | * |
||||||
635 | * Asserts that the command returns with an exit code of 0 |
||||||
636 | * |
||||||
637 | * Appends to the rebuild file allowing easy rerunning of the commmands in the test project |
||||||
638 | * |
||||||
639 | * @param string $bashCmds |
||||||
640 | * |
||||||
641 | * @throws Exception |
||||||
642 | */ |
||||||
643 | protected function execBash(string $bashCmds): void |
||||||
644 | { |
||||||
645 | fwrite(STDERR, "\n\t# Executing:\n\t$bashCmds"); |
||||||
646 | $startTime = microtime(true); |
||||||
647 | |||||||
648 | $fullCmds = ''; |
||||||
649 | if (!$this->isTravis()) { |
||||||
650 | $fullCmds .= "\n" . self::BASH_PHPNOXDEBUG_FUNCTION . "\n\n"; |
||||||
651 | } |
||||||
652 | $fullCmds .= "set -e;\n"; |
||||||
653 | $fullCmds .= "cd {$this->workDir};\n"; |
||||||
654 | #$fullCmds .= "exec 2>&1;\n"; |
||||||
655 | $fullCmds .= "$bashCmds\n"; |
||||||
656 | |||||||
657 | $output = []; |
||||||
658 | $exitCode = 0; |
||||||
659 | exec($fullCmds, $output, $exitCode); |
||||||
660 | |||||||
661 | if (0 !== $exitCode) { |
||||||
662 | throw new RuntimeException( |
||||||
663 | "Error running bash commands:\n\nOutput:\n----------\n\n" |
||||||
664 | . implode("\n", $output) |
||||||
665 | . "\n\nCommands:\n----------\n" |
||||||
666 | . str_replace( |
||||||
667 | "\n", |
||||||
668 | "\n\t", |
||||||
669 | "\n$bashCmds" |
||||||
670 | ) . "\n\n" |
||||||
671 | ); |
||||||
672 | } |
||||||
673 | |||||||
674 | $seconds = round(microtime(true) - $startTime, 2); |
||||||
675 | fwrite(STDERR, "\n\t\t#Completed in $seconds seconds\n"); |
||||||
676 | } |
||||||
677 | |||||||
678 | /** |
||||||
679 | * Generate all test entities |
||||||
680 | * |
||||||
681 | * @return array |
||||||
682 | */ |
||||||
683 | protected function generateEntities(): array |
||||||
684 | { |
||||||
685 | foreach (self::TEST_ENTITIES as $entityFqn) { |
||||||
686 | $this->generateEntity($entityFqn); |
||||||
687 | } |
||||||
688 | |||||||
689 | return self::TEST_ENTITIES; |
||||||
690 | } |
||||||
691 | |||||||
692 | protected function generateEntity(string $entityFqn): void |
||||||
693 | { |
||||||
694 | $namespace = self::TEST_PROJECT_ROOT_NAMESPACE; |
||||||
695 | $doctrineCmd = <<<DOCTRINE |
||||||
696 | dsm:generate:entity \ |
||||||
697 | --project-root-namespace="{$namespace}" \ |
||||||
698 | --entity-fully-qualified-name="{$entityFqn}" |
||||||
699 | DOCTRINE; |
||||||
700 | $this->execDoctrine($doctrineCmd); |
||||||
701 | } |
||||||
702 | |||||||
703 | protected function execDoctrine(string $doctrineCmd): void |
||||||
704 | { |
||||||
705 | $phpCmd = $this->isTravis() ? 'php' : 'phpNoXdebug'; |
||||||
706 | $bash = <<<BASH |
||||||
707 | $phpCmd bin/doctrine $doctrineCmd |
||||||
708 | BASH; |
||||||
709 | $error = false; |
||||||
710 | $message = ''; |
||||||
711 | try { |
||||||
712 | $this->execBash($bash); |
||||||
713 | } catch (RuntimeException $e) { |
||||||
714 | $this->addToRebuildFile("\n\n#The command below failed...\n\n"); |
||||||
715 | $error = true; |
||||||
716 | $message = $e->getMessage(); |
||||||
717 | } |
||||||
718 | $this->addToRebuildFile($bash); |
||||||
719 | self::assertFalse($error, $message); |
||||||
720 | } |
||||||
721 | |||||||
722 | /** |
||||||
723 | * Generate all test relations |
||||||
724 | * |
||||||
725 | * @return void |
||||||
726 | */ |
||||||
727 | protected function generateRelations(): void |
||||||
728 | { |
||||||
729 | foreach (self::TEST_RELATIONS as $relation) { |
||||||
730 | $this->setRelation(...$relation); |
||||||
0 ignored issues
–
show
|
|||||||
731 | } |
||||||
732 | } |
||||||
733 | |||||||
734 | protected function setRelation(string $entity1, string $type, string $entity2, bool $required): void |
||||||
735 | { |
||||||
736 | $namespace = self::TEST_PROJECT_ROOT_NAMESPACE; |
||||||
737 | $this->execDoctrine( |
||||||
738 | <<<DOCTRINE |
||||||
739 | dsm:set:relation \ |
||||||
740 | --project-root-path="{$this->workDir}" \ |
||||||
741 | --project-root-namespace="{$namespace}" \ |
||||||
742 | --entity1="{$entity1}" \ |
||||||
743 | --hasType="{$type}" \ |
||||||
744 | --entity2="{$entity2}" \ |
||||||
745 | --required-relation="{$required}" |
||||||
746 | DOCTRINE |
||||||
747 | ); |
||||||
748 | } |
||||||
749 | |||||||
750 | /** |
||||||
751 | * Generate one field per common type |
||||||
752 | * |
||||||
753 | * @return void |
||||||
754 | */ |
||||||
755 | protected function generateFields(): void |
||||||
756 | { |
||||||
757 | foreach (MappingHelper::COMMON_TYPES as $type) { |
||||||
758 | $fieldFqn = self::TEST_FIELD_TRAIT_NAMESPACE . '\\' . ucfirst($type); |
||||||
759 | $this->generateField($fieldFqn, $type); |
||||||
760 | } |
||||||
761 | foreach (self::UNIQUEABLE_FIELD_TYPES as $uniqueableType) { |
||||||
762 | $fieldFqn = self::TEST_FIELD_TRAIT_NAMESPACE . '\\Unique' . ucwords($uniqueableType); |
||||||
763 | $this->generateField($fieldFqn, $uniqueableType, null, true); |
||||||
764 | } |
||||||
765 | foreach (self::DUPLICATE_SHORT_NAME_FIELDS as $duplicateShortName) { |
||||||
766 | $this->generateField(...$duplicateShortName); |
||||||
0 ignored issues
–
show
The call to
EdmondsCommerce\Doctrine...geTest::generateField() has too few arguments starting with type .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
767 | } |
||||||
768 | } |
||||||
769 | |||||||
770 | /** |
||||||
771 | * @param string $propertyName |
||||||
772 | * @param string $type |
||||||
773 | * @param mixed|null $default |
||||||
774 | * @param bool $isUnique |
||||||
775 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
||||||
776 | */ |
||||||
777 | protected function generateField( |
||||||
778 | string $propertyName, |
||||||
779 | string $type, |
||||||
780 | $default = null, |
||||||
781 | bool $isUnique = false |
||||||
782 | ): void { |
||||||
783 | $namespace = self::TEST_PROJECT_ROOT_NAMESPACE; |
||||||
784 | $doctrineCmd = <<<DOCTRINE |
||||||
785 | dsm:generate:field \ |
||||||
786 | --project-root-path="{$this->workDir}" \ |
||||||
787 | --project-root-namespace="{$namespace}" \ |
||||||
788 | --field-fully-qualified-name="{$propertyName}" \ |
||||||
789 | --field-property-doctrine-type="{$type}" |
||||||
790 | DOCTRINE; |
||||||
791 | if (null !== $default) { |
||||||
792 | $doctrineCmd .= ' --' . GenerateFieldCommand::OPT_DEFAULT_VALUE . '="' . $default . '"' . "\\\n"; |
||||||
793 | } |
||||||
794 | if (true === $isUnique) { |
||||||
795 | $doctrineCmd .= ' --' . GenerateFieldCommand::OPT_IS_UNIQUE . "\\\n"; |
||||||
796 | } |
||||||
797 | $this->execDoctrine($doctrineCmd); |
||||||
798 | } |
||||||
799 | |||||||
800 | /** |
||||||
801 | * Set each field type on each entity type |
||||||
802 | * |
||||||
803 | * @param array $entities |
||||||
804 | * @param array $fields |
||||||
805 | * |
||||||
806 | * @return void |
||||||
807 | */ |
||||||
808 | protected function setFields(array $entities, array $fields): void |
||||||
809 | { |
||||||
810 | foreach ($entities as $entityFqn) { |
||||||
811 | foreach ($fields as $fieldFqn) { |
||||||
812 | $this->setField($entityFqn, $fieldFqn); |
||||||
813 | } |
||||||
814 | } |
||||||
815 | } |
||||||
816 | |||||||
817 | protected function setField(string $entityFqn, string $fieldFqn): void |
||||||
818 | { |
||||||
819 | $namespace = self::TEST_PROJECT_ROOT_NAMESPACE; |
||||||
820 | $doctrineCmd = <<<DOCTRINE |
||||||
821 | dsm:set:field \ |
||||||
822 | --project-root-path="{$this->workDir}" \ |
||||||
823 | --project-root-namespace="{$namespace}" \ |
||||||
824 | --entity="{$entityFqn}" \ |
||||||
825 | --field="{$fieldFqn}" |
||||||
826 | DOCTRINE; |
||||||
827 | $this->execDoctrine($doctrineCmd); |
||||||
828 | } |
||||||
829 | |||||||
830 | /** |
||||||
831 | * @return array |
||||||
832 | * @SuppressWarnings(PHPMD.StaticAccess) |
||||||
833 | */ |
||||||
834 | protected function getFieldFqns(): array |
||||||
835 | { |
||||||
836 | $fieldFqns = []; |
||||||
837 | foreach (MappingHelper::COMMON_TYPES as $type) { |
||||||
838 | $fieldFqns[] = self::TEST_FIELD_TRAIT_NAMESPACE . Inflector::classify($type) . 'FieldTrait'; |
||||||
839 | } |
||||||
840 | foreach (self::UNIQUEABLE_FIELD_TYPES as $type) { |
||||||
841 | $fieldFqns[] = self::TEST_FIELD_TRAIT_NAMESPACE . Inflector::classify('unique_' . $type) . 'FieldTrait'; |
||||||
842 | } |
||||||
843 | |||||||
844 | return $fieldFqns; |
||||||
845 | } |
||||||
846 | |||||||
847 | protected function setTheDuplicateNamedFields(array $entities): void |
||||||
848 | { |
||||||
849 | foreach ($entities as $k => $entityFqn) { |
||||||
850 | $fieldKey = ($k % 2 === 0) ? 0 : 1; |
||||||
851 | $this->setField($entityFqn, self::DUPLICATE_SHORT_NAME_FIELDS[$fieldKey][0]); |
||||||
852 | } |
||||||
853 | } |
||||||
854 | |||||||
855 | protected function setEmbeddable(string $entityFqn, string $embeddableTraitFqn): void |
||||||
856 | { |
||||||
857 | $doctrineCmd = <<<DOCTRINE |
||||||
858 | dsm:set:embeddable \ |
||||||
859 | --entity="{$entityFqn}" \ |
||||||
860 | --embeddable="{$embeddableTraitFqn}" |
||||||
861 | DOCTRINE; |
||||||
862 | $this->execDoctrine($doctrineCmd); |
||||||
863 | } |
||||||
864 | |||||||
865 | protected function generateEmbeddable(string $archetypeFqn, string $newClassName): void |
||||||
866 | { |
||||||
867 | $doctrineCmd = <<<DOCTRINE |
||||||
868 | dsm:generate:embeddable \ |
||||||
869 | --classname="{$newClassName}" \ |
||||||
870 | --archetype="{$archetypeFqn}" |
||||||
871 | DOCTRINE; |
||||||
872 | $this->execDoctrine($doctrineCmd); |
||||||
873 | } |
||||||
874 | |||||||
875 | protected function removeUnusedRelations(): void |
||||||
876 | { |
||||||
877 | $namespace = self::TEST_PROJECT_ROOT_NAMESPACE; |
||||||
878 | $doctrineCmd = <<<DOCTRINE |
||||||
879 | dsm:remove:unusedRelations \ |
||||||
880 | --project-root-namespace="{$namespace}" |
||||||
881 | DOCTRINE; |
||||||
882 | $this->execDoctrine($doctrineCmd); |
||||||
883 | } |
||||||
884 | |||||||
885 | /** |
||||||
886 | * @SuppressWarnings(PHPMD.Superglobals) |
||||||
887 | * @throws Exception |
||||||
888 | */ |
||||||
889 | public function testRunTests(): void |
||||||
890 | { |
||||||
891 | if ($this->isQuickTests()) { |
||||||
892 | $this->markTestSkipped('Quick tests is enabled'); |
||||||
893 | } |
||||||
894 | /** @lang bash */ |
||||||
895 | $bashCmds = <<<BASH |
||||||
896 | |||||||
897 | set +x |
||||||
898 | |||||||
899 | echo " |
||||||
900 | |||||||
901 | -------------------------------------------------- |
||||||
902 | STARTS Running Tests In {$this->workDir} |
||||||
903 | -------------------------------------------------- |
||||||
904 | |||||||
905 | " |
||||||
906 | |||||||
907 | #Prevent the retry tool dialogue etc |
||||||
908 | export CI=true |
||||||
909 | |||||||
910 | bash bin/qa |
||||||
911 | |||||||
912 | echo " |
||||||
913 | |||||||
914 | -------------------------------------------------- |
||||||
915 | DONE Running Tests In {$this->workDir} |
||||||
916 | -------------------------------------------------- |
||||||
917 | |||||||
918 | " |
||||||
919 | set -x |
||||||
920 | BASH; |
||||||
921 | $this->execBash($bashCmds); |
||||||
922 | } |
||||||
923 | } |
||||||
924 |
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.