Skip to content

Commit 6430b3c

Browse files
authored
Merge pull request #56 from magento-gl/Hammer-SVC-False-Positive-Build
AC-6945::SVC false-positive build failures
2 parents 3d2e6e5 + fc4ac2c commit 6430b3c

File tree

9 files changed

+263
-10
lines changed

9 files changed

+263
-10
lines changed

Diff for: dev/tests/Unit/ClassHierarchy/EntityTest.php

+54
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,21 @@ public function testIsTrait(string $type, bool $expected)
479479
);
480480
}
481481

482+
/**
483+
* @dataProvider dataProviderIsEnum
484+
* @param string $type
485+
* @param bool $expected
486+
*/
487+
public function testIsEnum(string $type, bool $expected)
488+
{
489+
$entity = new Entity('myName', $type);
490+
491+
$this->assertEquals(
492+
$expected,
493+
$entity->isEnum()
494+
);
495+
}
496+
482497
/*
483498
* Data providers
484499
*/
@@ -503,6 +518,10 @@ public static function dataProviderIsClass()
503518
Entity::TYPE_TRAIT,
504519
false,
505520
],
521+
'entity-is-enum-returns-false' => [
522+
Entity::TYPE_ENUM,
523+
false,
524+
],
506525
];
507526
}
508527

@@ -526,6 +545,10 @@ public static function dataProviderIsInterface()
526545
Entity::TYPE_TRAIT,
527546
false,
528547
],
548+
'entity-is-enum-returns-false' => [
549+
Entity::TYPE_ENUM,
550+
false,
551+
],
529552
];
530553
}
531554

@@ -549,6 +572,37 @@ public static function dataProviderIsTrait()
549572
Entity::TYPE_TRAIT,
550573
true,
551574
],
575+
'entity-is-enum-returns-false' => [
576+
Entity::TYPE_ENUM,
577+
false,
578+
],
579+
];
580+
}
581+
582+
/**
583+
* Provides test data for {@link EntityTest::testIsEnum()}
584+
*
585+
* @return array
586+
*/
587+
public static function dataProviderIsEnum()
588+
{
589+
return [
590+
'entity-is-class-returns-false' => [
591+
Entity::TYPE_CLASS,
592+
false,
593+
],
594+
'entity-is-interface-returns-false' => [
595+
Entity::TYPE_INTERFACE,
596+
false,
597+
],
598+
'entity-is-trait-returns-false' => [
599+
Entity::TYPE_TRAIT,
600+
false,
601+
],
602+
'entity-is-enum-returns-true' => [
603+
Entity::TYPE_ENUM,
604+
true,
605+
],
552606
];
553607
}
554608

Diff for: dev/tests/Unit/Console/Command/CompareSourceCommandApiClassesTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ public static function changesDataProvider()
5959
$pathToFixtures . '/new-method/source-code-before',
6060
$pathToFixtures . '/new-method/source-code-after',
6161
[
62-
'Class (MINOR)',
62+
'Class (PATCH)',
6363
'Test\Vcs\TestClass::testMethod | [public] Method has been added. | V015'
6464
],
65-
'Minor change is detected.'
65+
'Patch change is detected.'
6666
],
6767
'api-class-removed-class' => [
6868
$pathToFixtures . '/removed-class/source-code-before',

Diff for: src/Analyzer/DiXml/VirtualTypeAnalyzer.php

+62-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Magento\SemanticVersionChecker\Node\VirtualType;
1414
use Magento\SemanticVersionChecker\Operation\DiXml\VirtualTypeChanged;
1515
use Magento\SemanticVersionChecker\Operation\DiXml\VirtualTypeRemoved;
16+
use Magento\SemanticVersionChecker\Operation\DiXml\VirtualTypeToTypeChanged;
1617
use Magento\SemanticVersionChecker\Registry\XmlRegistry;
1718
use PHPSemVerChecker\Registry\Registry;
1819
use PHPSemVerChecker\Report\Report;
@@ -57,10 +58,15 @@ public function analyze($registryBefore, $registryAfter)
5758
foreach ($nodesBefore as $moduleName => $moduleNodes) {
5859
/* @var VirtualType $nodeBefore */
5960
$fileBefore = $registryBefore->mapping[XmlRegistry::NODES_KEY][$moduleName];
61+
62+
// Check if $moduleName exists in $registryAfter->mapping[XmlRegistry::NODES_KEY]
63+
if (!isset($registryAfter->mapping[XmlRegistry::NODES_KEY][$moduleName])) {
64+
continue;
65+
}
6066
foreach ($moduleNodes as $name => $nodeBefore) {
6167
// search nodesAfter the by name
6268
$nodeAfter = $nodesAfter[$moduleName][$name] ?? false;
63-
69+
$fileAfter = $registryAfter->mapping[XmlRegistry::NODES_KEY][$moduleName];
6470
if ($nodeAfter !== false && $nodeBefore !== $nodeAfter) {
6571
/* @var VirtualType $nodeAfter */
6672
$this->triggerNodeChange($nodeBefore, $nodeAfter, $fileBefore);
@@ -78,14 +84,68 @@ public function analyze($registryBefore, $registryAfter)
7884
}
7985
}
8086

81-
$operation = new VirtualTypeRemoved($fileBefore, $name);
87+
$finalPath = $this->convertClassNameToFilePath($fileAfter, $name, '.php');
88+
89+
if (file_exists($finalPath)) {
90+
$operation = new VirtualTypeToTypeChanged($fileBefore, $name);
91+
} else {
92+
$operation = new VirtualTypeRemoved($fileBefore, $name);
93+
}
8294
$this->report->add('di', $operation);
8395
}
8496
}
8597

8698
return $this->report;
8799
}
88100

101+
/**
102+
* Method to convert class name to file path
103+
*
104+
* @param string $filePath
105+
* @param string $className
106+
* @param string $extraString
107+
* @return string
108+
*/
109+
private function convertClassNameToFilePath($filePath, $className, $extraString = ''): string
110+
{
111+
// Split the initial file path to get the base directory.
112+
$parts = explode('/', $filePath);
113+
$classParts = explode('\\', $className);
114+
115+
// Find the common part between the file path and class name.
116+
$baseDirParts = [];
117+
foreach ($parts as $part) {
118+
$baseDirParts[] = $part;
119+
120+
if (in_array($part, $classParts)) {
121+
break;
122+
}
123+
}
124+
125+
// Reconstruct the base directory path.
126+
$baseDir = implode('/', $baseDirParts);
127+
128+
// Replace namespace separators with directory separators in the class name.
129+
$classFilePath = str_replace('\\', '/', $className);
130+
131+
$position = strpos($classFilePath, "/");
132+
133+
if ($position !== false) {
134+
$classFilePath = substr($classFilePath, $position);
135+
}
136+
137+
// Combine the base directory and class file path.
138+
$fullPath = rtrim($baseDir, '/') . $classFilePath;
139+
140+
141+
// Append the extra string if provided.
142+
if ($extraString) {
143+
$fullPath .= $extraString;
144+
}
145+
return $fullPath;
146+
}
147+
148+
89149
/**
90150
* Return a filtered node list from type {@link VirtualType}
91151
*

Diff for: src/ClassHierarchy/DependencyGraph.php

+16
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,20 @@ public function findOrCreateTrait(string $fullyQualifiedName): Entity
103103

104104
return $trait;
105105
}
106+
107+
/**
108+
* @param string $fullyQualifiedName
109+
* @return Entity
110+
*/
111+
public function findOrCreateEnum(string $fullyQualifiedName): Entity
112+
{
113+
$enum = $this->findEntityByName($fullyQualifiedName);
114+
115+
if (!$enum) {
116+
$enum = $this->entityFactory->createEnum($fullyQualifiedName);
117+
$this->addEntity($enum);
118+
}
119+
120+
return $enum;
121+
}
106122
}

Diff for: src/ClassHierarchy/DependencyInspectionVisitor.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Magento\SemanticVersionChecker\Helper\Node as NodeHelper;
1313
use PhpParser\Node;
14+
use PhpParser\Node\Stmt\Enum_ as EnumNode;
1415
use PhpParser\Node\Stmt\Class_ as ClassNode;
1516
use PhpParser\Node\Stmt\ClassLike;
1617
use PhpParser\Node\Stmt\ClassMethod;
@@ -22,7 +23,7 @@
2223
use PhpParser\NodeVisitorAbstract;
2324

2425
/**
25-
* Implements a visitor for `class`, `interface` and `trait` nodes that generates a dependency graph.
26+
* Implements a visitor for `class`, `interface`, `trait` and `enum` nodes that generates a dependency graph.
2627
*/
2728
class DependencyInspectionVisitor extends NodeVisitorAbstract
2829
{
@@ -94,8 +95,8 @@ public function enterNode(Node $node)
9495
}
9596

9697
/**
97-
* Handles Class, Interface, and Traits nodes. Sets currentClassLike entity and will populate extends, implements,
98-
* and API information
98+
* Handles Class, Interface, Traits and Enum nodes. Sets currentClassLike entity and will populate extends,
99+
* implements, and API information
99100
*
100101
* @param ClassLike $node
101102
* @return int|null
@@ -135,6 +136,9 @@ private function handleClassLike(ClassLike $node)
135136
case $node instanceof TraitNode:
136137
$this->currentClassLike = $this->dependencyGraph->findOrCreateTrait((string)$namespacedName);
137138
break;
139+
case $node instanceof EnumNode:
140+
$this->currentClassLike = $this->dependencyGraph->findOrCreateEnum((string)$namespacedName);
141+
break;
138142
}
139143
$this->currentClassLike->setIsApi($this->nodeHelper->isApiNode($node));
140144
return null;

Diff for: src/ClassHierarchy/Entity.php

+12-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use PhpParser\Node\Stmt\PropertyProperty;
1515

1616
/**
17-
* Implements an entity that reflects a `class`, `interface` or `trait` and its dependencies.
17+
* Implements an entity that reflects a `class`, `interface`, `enum` or `trait` and its dependencies.
1818
*/
1919
class Entity
2020
{
@@ -24,6 +24,7 @@ class Entity
2424
public const TYPE_CLASS = 'class';
2525
public const TYPE_INTERFACE = 'interface';
2626
public const TYPE_TRAIT = 'trait';
27+
public const TYPE_ENUM = 'enum';
2728
/**#@-*/
2829

2930
/**
@@ -327,6 +328,16 @@ public function isTrait(): bool
327328
return $this->type === self::TYPE_TRAIT;
328329
}
329330

331+
/**
332+
* Reflects whether current entity reflects an `enum`.
333+
*
334+
* @return bool
335+
*/
336+
public function isEnum(): bool
337+
{
338+
return $this->type === self::TYPE_ENUM;
339+
}
340+
330341
/*
331342
* Private methods
332343
*/

Diff for: src/ClassHierarchy/EntityFactory.php

+9
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,13 @@ public function createTrait(string $name): Entity
4040
{
4141
return new Entity($name, Entity::TYPE_TRAIT);
4242
}
43+
44+
/**
45+
* @param string $name
46+
* @return Entity
47+
*/
48+
public function createEnum(string $name): Entity
49+
{
50+
return new Entity($name, Entity::TYPE_ENUM);
51+
}
4352
}

Diff for: src/Operation/DiXml/VirtualTypeToTypeChanged.php

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Magento\SemanticVersionChecker\Operation\DiXml;
11+
12+
use PHPSemVerChecker\Operation\Operation;
13+
use PHPSemVerChecker\SemanticVersioning\Level;
14+
15+
/**
16+
* When a virtual type was changed.
17+
*/
18+
class VirtualTypeToTypeChanged extends Operation
19+
{
20+
/**
21+
* Error codes.
22+
*
23+
* @var array
24+
*/
25+
protected $code = 'M201';
26+
27+
/**
28+
* Change level.
29+
*
30+
* @var int
31+
*/
32+
protected $level = Level::PATCH;
33+
34+
/**
35+
* Operation message.
36+
*
37+
* @var string
38+
*/
39+
protected $reason = 'Virtual Type was changed to type';
40+
/**
41+
* File path before changes.
42+
*
43+
* @var string
44+
*/
45+
protected $fileBefore;
46+
47+
/**
48+
* Property context before changes.
49+
*
50+
* @var \PhpParser\Node\Stmt
51+
*/
52+
protected $contextBefore;
53+
54+
/**
55+
* Property before changes.
56+
*
57+
* @var \PhpParser\Node\Stmt\Property
58+
*/
59+
protected $propertyBefore;
60+
61+
/**
62+
* @param string $fileBefore
63+
* @param string $target
64+
*/
65+
public function __construct($fileBefore, $target)
66+
{
67+
$this->fileBefore = $fileBefore;
68+
$this->target = $target;
69+
}
70+
71+
/**
72+
* Returns file path before changes.
73+
*
74+
* @return string
75+
*/
76+
public function getLocation(): string
77+
{
78+
return $this->fileBefore;
79+
}
80+
81+
/**
82+
* Returns line position of existed property.
83+
*
84+
* @return int
85+
*/
86+
public function getLine(): int
87+
{
88+
return 0;
89+
}
90+
/**
91+
* Get level.
92+
*
93+
* @return mixed
94+
*/
95+
public function getLevel(): int
96+
{
97+
return $this->level;
98+
}
99+
}

0 commit comments

Comments
 (0)