forked from octobercms/october
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPluginTestCase.php
201 lines (168 loc) · 5.5 KB
/
PluginTestCase.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
<?php
use System\Classes\UpdateManager;
use System\Classes\PluginManager;
use October\Rain\Database\Model as ActiveRecord;
abstract class PluginTestCase extends Illuminate\Foundation\Testing\TestCase
{
/**
* @var array Cache for storing which plugins have been loaded
* and refreshed.
*/
protected $pluginTestCaseLoadedPlugins = [];
/**
* Creates the application.
* @return Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
$app['cache']->setDefaultDriver('array');
$app->setLocale('en');
/*
* Store database in memory
*/
$app['config']->set('database.default', 'sqlite');
$app['config']->set('database.connections.sqlite', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => ''
]);
/*
* Modify the plugin path away from the test context
*/
$app->setPluginsPath(realpath(base_path().Config::get('cms.pluginsPath')));
return $app;
}
/**
* Perform test case set up.
* @return void
*/
public function setUp()
{
/*
* Create application instance
*/
parent::setUp();
/*
* Rebind Laravel container in October Singletons
*/
UpdateManager::instance()->bindContainerObjects();
PluginManager::instance()->bindContainerObjects();
/*
* Ensure system is up to date
*/
$this->runOctoberUpCommand();
/*
* Detect plugin from test and autoload it
*/
$this->pluginTestCaseLoadedPlugins = [];
$pluginCode = $this->guessPluginCodeFromTest();
if ($pluginCode !== false) {
$this->runPluginRefreshCommand($pluginCode, false);
}
/*
* Disable mailer
*/
Mail::pretend();
}
/**
* Flush event listeners and collect garbage.
* @return void
*/
public function tearDown()
{
$this->flushModelEventListeners();
parent::tearDown();
unset($this->app);
}
/**
* Migrate database using october:up command.
* @return void
*/
protected function runOctoberUpCommand()
{
Artisan::call('october:up');
}
/**
* Since the test environment has loaded all the test plugins
* natively, this method will ensure the desired plugin is
* loaded in the system before proceeding to migrate it.
* @return void
*/
protected function runPluginRefreshCommand($code, $throwException = true)
{
if (!preg_match('/^[\w+]*\.[\w+]*$/', $code)) {
if (!$throwException) return;
throw new Exception(sprintf('Invalid plugin code: "%s"', $code));
}
$manager = PluginManager::instance();
$plugin = $manager->findByIdentifier($code);
/*
* First time seeing this plugin, load it up
*/
if (!$plugin) {
$namespace = '\\'.str_replace('.', '\\', strtolower($code));
$path = array_get($manager->getPluginNamespaces(), $namespace);
if (!$path) {
if (!$throwException) return;
throw new Exception(sprintf('Unable to find plugin with code: "%s"', $code));
}
$plugin = $manager->loadPlugin($namespace, $path);
}
/*
* Spin over dependencies and refresh them too
*/
$this->pluginTestCaseLoadedPlugins[$code] = $plugin;
if (!empty($plugin->require)) {
foreach ((array) $plugin->require as $dependency) {
if (isset($this->pluginTestCaseLoadedPlugins[$dependency])) continue;
$this->runPluginRefreshCommand($dependency);
}
}
/*
* Execute the command
*/
Artisan::call('plugin:refresh', ['name' => $code]);
}
/**
* The models in October use a static property to store their events, these
* will need to be targeted and reset ready for a new test cycle.
* Pivot models are an exception since they are internally managed.
* @return void
*/
protected function flushModelEventListeners()
{
foreach (get_declared_classes() as $class) {
if ($class == 'October\Rain\Database\Pivot') {
continue;
}
$reflectClass = new ReflectionClass($class);
if (
!$reflectClass->isInstantiable() ||
!$reflectClass->isSubclassOf('October\Rain\Database\Model') ||
$reflectClass->isSubclassOf('October\Rain\Database\Pivot')
) {
continue;
}
$class::flushEventListeners();
}
ActiveRecord::flushEventListeners();
}
/**
* Locates the plugin code based on the test file location.
* @return string|bool
*/
protected function guessPluginCodeFromTest()
{
$reflect = new ReflectionClass($this);
$path = $reflect->getFilename();
$basePath = $this->app->pluginsPath();
$result = false;
if (strpos($path, $basePath) === 0) {
$result = ltrim(str_replace('\\', '/', substr($path, strlen($basePath))), '/');
$result = implode('.', array_slice(explode('/', $result), 0, 2));
}
return $result;
}
}