Passed
Push — trunk ( 60c287...522604 )
by Christian
21:52 queued 08:22
created

FeatureFlagExtension   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 44
c 0
b 0
f 0
dl 0
loc 102
rs 10
wmc 18

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A executeAfterTest() 0 24 5
C executeBeforeTest() 0 53 12
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Core\Test\PHPUnit\Extension;
4
5
use Doctrine\Common\Annotations\AnnotationReader;
6
use PHPUnit\Runner\AfterTestHook;
7
use PHPUnit\Runner\BeforeTestHook;
8
use Shopware\Core\Framework\Feature;
9
use Shopware\Core\Framework\Log\Package;
10
use Shopware\Core\Test\Annotation\DisabledFeatures;
11
use Shopware\Tests\Unit\Core\Test\FeatureFlagExtensionTest;
12
13
/**
14
 * @internal
15
 * This extension guarantees a clean feature environment for pure unit tests
16
 */
17
#[Package('core')]
18
class FeatureFlagExtension implements BeforeTestHook, AfterTestHook
19
{
20
    private readonly AnnotationReader $annotationReader;
21
22
    /**
23
     * @var array<mixed>|null
24
     */
25
    private ?array $savedFeatureConfig = null;
26
27
    /**
28
     * @var array<mixed>|null
29
     */
30
    private ?array $savedServerVars = null;
31
32
    public function __construct(
33
        private readonly string $namespacePrefix = 'Shopware\\Tests\\Unit\\',
34
        private readonly bool $testMode = false
35
    ) {
36
        $this->annotationReader = new AnnotationReader();
0 ignored issues
show
Bug introduced by
The property annotationReader is declared read-only in Shopware\Core\Test\PHPUn...on\FeatureFlagExtension.
Loading history...
37
    }
38
39
    public function executeBeforeTest(string $test): void
40
    {
41
        preg_match('/([^:]+)::([^$ ]+)($| )/', $test, $matches);
42
43
        if (empty($matches)) {
44
            return;
45
        }
46
47
        $class = $matches[1];
48
        $method = $matches[2];
49
50
        // do not run when this class is unit tested
51
        if (!$this->testMode && $class === FeatureFlagExtensionTest::class) {
52
            // @codeCoverageIgnoreStart
53
            return;
54
            // @codeCoverageIgnoreEnd
55
        }
56
57
        $reflectedMethod = new \ReflectionMethod($class, $method);
58
59
        /** @var DisabledFeatures[] $features */
60
        $features = array_filter([
61
            $this->annotationReader->getMethodAnnotation($reflectedMethod, DisabledFeatures::class) ?? [],
62
            $this->annotationReader->getClassAnnotation($reflectedMethod->getDeclaringClass(), DisabledFeatures::class) ?? [],
63
        ]);
64
65
        $this->savedFeatureConfig = null;
66
67
        if (!str_starts_with($class, $this->namespacePrefix)) {
68
            return;
69
        }
70
71
        $this->savedFeatureConfig = Feature::getRegisteredFeatures();
72
        $this->savedServerVars = $_SERVER;
73
74
        Feature::resetRegisteredFeatures();
75
        foreach ($_SERVER as $key => $value) {
76
            if (str_starts_with($key, 'v6.') || str_starts_with($key, 'FEATURE_') || str_starts_with($key, 'V6_')) {
77
                // set to false so that $_ENV is not checked
78
                $_SERVER[$key] = false;
79
            }
80
        }
81
82
        $disabledFlags = [];
83
        foreach ($features as $feature) {
84
            foreach ($feature->features as $featureName) {
85
                $disabledFlags[Feature::normalizeName($featureName)] = true;
86
            }
87
        }
88
89
        foreach ($this->savedFeatureConfig as $flag => $config) {
90
            $flag = Feature::normalizeName($flag);
91
            $_SERVER[$flag] = !\array_key_exists($flag, $disabledFlags);
92
        }
93
    }
94
95
    public function executeAfterTest(string $test, float $time): void
96
    {
97
        preg_match('/([^:]+)::([^$ ]+)($| )/', $test, $matches);
98
99
        if (empty($matches)) {
100
            return;
101
        }
102
103
        $class = $matches[1];
104
105
        // do not run when this class is unit tested
106
        if (!$this->testMode && $class === FeatureFlagExtensionTest::class) {
107
            // @codeCoverageIgnoreStart
108
            return;
109
            // @codeCoverageIgnoreEnd
110
        }
111
112
        if ($this->savedFeatureConfig === null) {
113
            return;
114
        }
115
116
        $_SERVER = $this->savedServerVars;
117
        Feature::resetRegisteredFeatures();
118
        Feature::registerFeatures($this->savedFeatureConfig);
119
    }
120
}
121