winter/tests/PluginTestCase.php
Samuel Georges 5f2f913732 Fixes unit tests
Clearing the cache to allow failures
Substitute mail pretend with log mode
2017-05-12 07:20:59 +10:00

217 lines
5.9 KiB
PHP

<?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' => ''
]);
/*
* Mailer pretend
*/
$app['config']->set('mail.driver', 'log');
/*
* 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);
}
}
/**
* 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]);
}
/**
* Returns a plugin object from its code, useful for registering events, etc.
* @return PluginBase
*/
protected function getPluginObject($code = null)
{
if ($code === null) {
$code = $this->guessPluginCodeFromTest();
}
if (isset($this->pluginTestCaseLoadedPlugins[$code])) {
return $this->pluginTestCaseLoadedPlugins[$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;
}
}