Passed
Push — 4 ( 77a45c...ec956a )
by Damian
07:50 queued 13s
created

FixtureTestState::loadFixture()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Dev\State;
4
5
use LogicException;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Core\Manifest\ClassLoader;
9
use SilverStripe\Dev\FixtureFactory;
10
use SilverStripe\Dev\SapphireTest;
11
use SilverStripe\Dev\YamlFixture;
12
use SilverStripe\ORM\DataObject;
13
14
class FixtureTestState implements TestState
15
{
16
17
    /**
18
     * @var FixtureFactory[]
19
     */
20
    private $fixtureFactories = [];
21
22
    /**
23
     * Called on setup
24
     *
25
     * @param SapphireTest $test
26
     */
27
    public function setUp(SapphireTest $test)
28
    {
29
        if ($this->testNeedsDB($test)) {
30
            $tmpDB = $test::tempDB();
31
            if (!$tmpDB->isUsed()) {
32
                $tmpDB->build();
33
            }
34
            DataObject::singleton()->flushCache();
35
36
            if (!$tmpDB->hasStarted()) {
37
                foreach ($test->getRequireDefaultRecordsFrom() as $className) {
38
                    $instance = singleton($className);
39
                    if (method_exists($instance, 'requireDefaultRecords')) {
40
                        $instance->requireDefaultRecords();
41
                    }
42
                    if (method_exists($instance, 'augmentDefaultRecords')) {
43
                        $instance->augmentDefaultRecords();
44
                    }
45
                }
46
                $this->loadFixtures($test);
47
            }
48
            $tmpDB->startTransaction();
49
        }
50
    }
51
52
    /**
53
     * Called on tear down
54
     *
55
     * @param SapphireTest $test
56
     */
57
    public function tearDown(SapphireTest $test)
58
    {
59
        if ($this->testNeedsDB($test)) {
60
            $test::tempDB()->rollbackTransaction();
61
        }
62
    }
63
64
    /**
65
     * Called once on setup
66
     *
67
     * @param string $class Class being setup
68
     */
69
    public function setUpOnce($class)
70
    {
71
        $this->fixtureFactories[strtolower($class)] = Injector::inst()->create(FixtureFactory::class);
72
    }
73
74
    /**
75
     * Called once on tear down
76
     *
77
     * @param string $class Class being torn down
78
     */
79
    public function tearDownOnce($class)
80
    {
81
        unset($this->fixtureFactories[strtolower($class)]);
82
        $class::tempDB()->clearAllData();
83
    }
84
85
    /**
86
     * @param string $class
87
     *
88
     * @return bool|FixtureFactory
89
     */
90
    public function getFixtureFactory($class)
91
    {
92
        $testClass = strtolower($class);
93
        if (array_key_exists($testClass, $this->fixtureFactories)) {
94
            return $this->fixtureFactories[$testClass];
95
        }
96
        return false;
97
    }
98
99
    /**
100
     * @param FixtureFactory $factory
101
     * @param string $class
102
     */
103
    public function setFixtureFactory(FixtureFactory $factory, $class)
104
    {
105
        $this->fixtureFactories[strtolower($class)] = $factory;
106
    }
107
108
    /**
109
     * @param array $fixtures
110
     *
111
     * @param SapphireTest $test
112
     *
113
     * @return array
114
     */
115
    protected function getFixturePaths($fixtures, SapphireTest $test)
116
    {
117
        return array_map(function ($fixtureFilePath) use ($test) {
118
            return $this->resolveFixturePath($fixtureFilePath, $test);
119
        }, $fixtures);
120
    }
121
122
    /**
123
     * @param SapphireTest $test
124
     */
125
    protected function loadFixtures(SapphireTest $test)
126
    {
127
        $fixtures = $test::get_fixture_file();
128
        $fixtures = is_array($fixtures) ? $fixtures : [$fixtures];
0 ignored issues
show
introduced by
The condition is_array($fixtures) is always false.
Loading history...
129
        $paths = $this->getFixturePaths($fixtures, $test);
130
        foreach ($paths as $fixtureFile) {
131
            $this->loadFixture($fixtureFile, $test);
132
        }
133
    }
134
135
    /**
136
     * @param string $fixtureFile
137
     * @param SapphireTest $test
138
     */
139
    protected function loadFixture($fixtureFile, SapphireTest $test)
140
    {
141
        /** @var YamlFixture $fixture */
142
        $fixture = Injector::inst()->create(YamlFixture::class, $fixtureFile);
143
        $fixture->writeInto($this->getFixtureFactory(get_class($test)));
0 ignored issues
show
Bug introduced by
It seems like $this->getFixtureFactory(get_class($test)) can also be of type false; however, parameter $factory of SilverStripe\Dev\YamlFixture::writeInto() does only seem to accept SilverStripe\Dev\FixtureFactory, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
        $fixture->writeInto(/** @scrutinizer ignore-type */ $this->getFixtureFactory(get_class($test)));
Loading history...
144
    }
145
146
    /**
147
     * Map a fixture path to a physical file
148
     *
149
     * @param string $fixtureFilePath
150
     * @param SapphireTest $test
151
     *
152
     * @return string
153
     */
154
    protected function resolveFixturePath($fixtureFilePath, SapphireTest $test)
155
    {
156
        // Support fixture paths relative to the test class, rather than relative to webroot
157
        // String checking is faster than file_exists() calls.
158
        $isRelativeToFile
159
            = (strpos($fixtureFilePath, '/') === false)
160
            || preg_match('/^(\.){1,2}/', $fixtureFilePath);
161
162
        if ($isRelativeToFile) {
163
            $resolvedPath = realpath($this->getTestAbsolutePath($test) . '/' . $fixtureFilePath);
164
            if ($resolvedPath) {
165
                return $resolvedPath;
166
            }
167
        }
168
169
        // Check if file exists relative to base dir
170
        $resolvedPath = realpath(Director::baseFolder() . '/' . $fixtureFilePath);
171
        if ($resolvedPath) {
172
            return $resolvedPath;
173
        }
174
175
        return $fixtureFilePath;
176
    }
177
178
    /**
179
     * Useful for writing unit tests without hardcoding folder structures.
180
     *
181
     * @param SapphireTest $test
182
     *
183
     * @return string Absolute path to current class.
184
     */
185
    protected function getTestAbsolutePath(SapphireTest $test)
186
    {
187
        $filename = ClassLoader::inst()->getItemPath(get_class($test));
188
        if (!$filename) {
189
            throw new LogicException('getItemPath returned null for ' . static::class
190
                . '. Try adding flush=1 to the test run.');
191
        }
192
        return dirname($filename);
193
    }
194
195
    /**
196
     * @param SapphireTest $test
197
     *
198
     * @return bool
199
     */
200
    protected function testNeedsDB(SapphireTest $test)
201
    {
202
        $annotations = $test->getAnnotations();
203
204
        // annotation explicitly disables the DB
205
        if (array_key_exists('useDatabase', $annotations['method'])
206
            && $annotations['method']['useDatabase'][0] === 'false') {
207
            return false;
208
        }
209
210
        // annotation explicitly enables the DB
211
        if (array_key_exists('useDatabase', $annotations['method'])
212
            && $annotations['method']['useDatabase'][0] !== 'false') {
213
            return true;
214
        }
215
216
        // test class explicitly enables DB
217
        if ($test->getUsesDatabase()) {
218
            return true;
219
        }
220
221
        // presence of fixture file implicitly enables DB
222
        $fixtures = $test::get_fixture_file();
223
        if (!empty($fixtures)) {
224
            return true;
225
        }
226
227
        return false;
228
    }
229
}
230