init
This commit is contained in:
288
vendor/codeception/specify/src/Codeception/Specify.php
vendored
Normal file
288
vendor/codeception/specify/src/Codeception/Specify.php
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
namespace Codeception;
|
||||
|
||||
use Codeception\Specify\Config;
|
||||
use Codeception\Specify\ConfigBuilder;
|
||||
use Codeception\Specify\ObjectProperty;
|
||||
|
||||
trait Specify
|
||||
{
|
||||
|
||||
private $beforeSpecify = array();
|
||||
private $afterSpecify = array();
|
||||
|
||||
/**
|
||||
* @var Specify\Config
|
||||
*/
|
||||
private $specifyConfig;
|
||||
|
||||
/**
|
||||
* @var \DeepCopy\DeepCopy()
|
||||
*/
|
||||
private $copier;
|
||||
|
||||
private function specifyInit()
|
||||
{
|
||||
if ($this->copier) return;
|
||||
$this->copier = new \DeepCopy\DeepCopy();
|
||||
$this->copier->skipUncloneable();
|
||||
if (!$this->specifyConfig) $this->specifyConfig = Config::create();
|
||||
}
|
||||
|
||||
function specify($specification, \Closure $callable = null, $params = [])
|
||||
{
|
||||
if (!$callable) return;
|
||||
$this->specifyInit();
|
||||
|
||||
$test = $callable->bindTo($this);
|
||||
$oldName = $this->getName();
|
||||
$newName = $oldName . ' | ' . $specification;
|
||||
|
||||
$this->setName($newName);
|
||||
|
||||
$properties = $this->getSpecifyObjectProperties();
|
||||
|
||||
// prepare for execution
|
||||
$throws = $this->getSpecifyExpectedException($params);
|
||||
$examples = $this->getSpecifyExamples($params);
|
||||
$showExamplesIndex = $examples !== [[]];
|
||||
|
||||
foreach ($examples as $idx => $example) {
|
||||
if ($showExamplesIndex) {
|
||||
$this->setName($newName . ' | examples index ' . $idx);
|
||||
}
|
||||
|
||||
// copy current object properties
|
||||
$this->specifyCloneProperties($properties);
|
||||
|
||||
if (!empty($this->beforeSpecify) && is_array($this->beforeSpecify)) {
|
||||
foreach ($this->beforeSpecify as $closure) {
|
||||
if ($closure instanceof \Closure) $closure->__invoke();
|
||||
}
|
||||
}
|
||||
|
||||
$this->specifyExecute($test, $throws, $example);
|
||||
|
||||
// restore object properties
|
||||
$this->specifyRestoreProperties($properties);
|
||||
|
||||
if (!empty($this->afterSpecify) && is_array($this->afterSpecify)) {
|
||||
foreach ($this->afterSpecify as $closure) {
|
||||
if ($closure instanceof \Closure) $closure->__invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore test name
|
||||
$this->setName($oldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $params
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function getSpecifyExamples($params)
|
||||
{
|
||||
if (isset($params['examples'])) {
|
||||
if (!is_array($params['examples'])) throw new \RuntimeException("Examples should be an array");
|
||||
return $params['examples'];
|
||||
}
|
||||
return [[]];
|
||||
}
|
||||
|
||||
private function getSpecifyExpectedException($params)
|
||||
{
|
||||
if (isset($params['throws'])) {
|
||||
$throws = (is_array($params['throws'])) ? $params['throws'][0] : $params['throws'];
|
||||
|
||||
if (is_object($throws)) {
|
||||
$throws = get_class($throws);
|
||||
}
|
||||
if ($throws === 'fail') {
|
||||
$throws = 'PHPUnit_Framework_AssertionFailedError';
|
||||
}
|
||||
|
||||
$message = (is_array($params['throws']) && isset($params['throws'][1])) ? $params['throws'][1] : false;
|
||||
|
||||
return [$throws, $message];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function specifyExecute($test, $throws = false, $examples = array())
|
||||
{
|
||||
$message = false;
|
||||
|
||||
if (is_array($throws)) {
|
||||
$message = ($throws[1]) ? strtolower($throws[1]) : false;
|
||||
$throws = $throws[0];
|
||||
}
|
||||
|
||||
$result = $this->getTestResultObject();
|
||||
|
||||
try {
|
||||
call_user_func_array($test, $examples);
|
||||
$this->specifyCheckMockObjects();
|
||||
} catch (\PHPUnit_Framework_AssertionFailedError $e) {
|
||||
if ($throws !== get_class($e)){
|
||||
$result->addFailure(clone($this), $e, $result->time());
|
||||
}
|
||||
|
||||
if ($message !==false && $message !== strtolower($e->getMessage())) {
|
||||
$f = new \PHPUnit_Framework_AssertionFailedError("exception message '$message' was expected, but '" . $e->getMessage() . "' was received");
|
||||
$result->addFailure(clone($this), $f, $result->time());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($throws) {
|
||||
if ($throws !== get_class($e)) {
|
||||
$f = new \PHPUnit_Framework_AssertionFailedError("exception '$throws' was expected, but " . get_class($e) . ' was thrown');
|
||||
$result->addFailure(clone($this), $f, $result->time());
|
||||
}
|
||||
|
||||
if ($message !==false && $message !== strtolower($e->getMessage())) {
|
||||
$f = new \PHPUnit_Framework_AssertionFailedError("exception message '$message' was expected, but '" . $e->getMessage() . "' was received");
|
||||
$result->addFailure(clone($this), $f, $result->time());
|
||||
}
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($throws) {
|
||||
if (isset($e)) {
|
||||
$this->assertTrue(true, 'exception handled');
|
||||
} else {
|
||||
$f = new \PHPUnit_Framework_AssertionFailedError("exception '$throws' was not thrown as expected");
|
||||
$result->addFailure(clone($this), $f, $result->time());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function specifyConfig()
|
||||
{
|
||||
if (!$this->specifyConfig) $this->specifyConfig = Config::create();
|
||||
return new ConfigBuilder($this->specifyConfig);
|
||||
}
|
||||
|
||||
function beforeSpecify(\Closure $callable = null)
|
||||
{
|
||||
$this->beforeSpecify[] = $callable->bindTo($this);
|
||||
}
|
||||
|
||||
function afterSpecify(\Closure $callable = null)
|
||||
{
|
||||
$this->afterSpecify[] = $callable->bindTo($this);
|
||||
}
|
||||
|
||||
function cleanSpecify()
|
||||
{
|
||||
$this->beforeSpecify = $this->afterSpecify = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectProperty[] $properties
|
||||
*/
|
||||
private function specifyCloneProperties($properties)
|
||||
{
|
||||
foreach ($properties as $property) {
|
||||
$propertyName = $property->getName();
|
||||
$propertyValue = $property->getValue();
|
||||
|
||||
if ($this->specifyConfig->classIgnored($propertyValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->specifyConfig->propertyIsShallowCloned($propertyName)) {
|
||||
if (is_object($propertyValue)) {
|
||||
$property->setValue(clone $propertyValue);
|
||||
} else {
|
||||
$property->setValue($propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->specifyConfig->propertyIsDeeplyCloned($propertyName)) {
|
||||
$property->setValue($this->copier->copy($propertyValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectProperty[] $properties
|
||||
*/
|
||||
private function specifyRestoreProperties($properties)
|
||||
{
|
||||
foreach ($properties as $property) {
|
||||
$property->restoreValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectProperty[]
|
||||
*/
|
||||
private function getSpecifyObjectProperties()
|
||||
{
|
||||
$objectReflection = new \ReflectionObject($this);
|
||||
$propertiesToClone = $objectReflection->getProperties();
|
||||
|
||||
if (($classProperties = $this->specifyGetClassPrivateProperties()) !== []) {
|
||||
$propertiesToClone = array_merge($propertiesToClone, $classProperties);
|
||||
}
|
||||
|
||||
$properties = [];
|
||||
|
||||
foreach ($propertiesToClone as $property) {
|
||||
if ($this->specifyConfig->propertyIgnored($property->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$properties[] = new ObjectProperty($this, $property);
|
||||
}
|
||||
|
||||
// isolate mockObjects property from PHPUnit_Framework_TestCase
|
||||
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) {
|
||||
$properties[] = $mockObjects = new ObjectProperty(
|
||||
$this, $phpUnitReflection->getProperty('mockObjects')
|
||||
);
|
||||
|
||||
// remove all mock objects inherited from parent scope(s)
|
||||
$mockObjects->setValue([]);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
private function specifyCheckMockObjects()
|
||||
{
|
||||
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) {
|
||||
$verifyMockObjects = $phpUnitReflection->getMethod('verifyMockObjects');
|
||||
$verifyMockObjects->setAccessible(true);
|
||||
$verifyMockObjects->invoke($this);
|
||||
}
|
||||
}
|
||||
|
||||
private function specifyGetClassPrivateProperties()
|
||||
{
|
||||
static $properties = [];
|
||||
|
||||
if (!isset($properties[__CLASS__])) {
|
||||
$reflection = new \ReflectionClass(__CLASS__);
|
||||
|
||||
$properties[__CLASS__] = (get_class($this) !== __CLASS__)
|
||||
? $reflection->getProperties(\ReflectionProperty::IS_PRIVATE) : [];
|
||||
}
|
||||
|
||||
return $properties[__CLASS__];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ReflectionClass|null
|
||||
*/
|
||||
private function specifyGetPhpUnitReflection()
|
||||
{
|
||||
if ($this instanceof \PHPUnit_Framework_TestCase) {
|
||||
return new \ReflectionClass('\PHPUnit_Framework_TestCase');
|
||||
}
|
||||
}
|
||||
}
|
||||
156
vendor/codeception/specify/src/Codeception/Specify/Config.php
vendored
Normal file
156
vendor/codeception/specify/src/Codeception/Specify/Config.php
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
namespace Codeception\Specify;
|
||||
|
||||
/**
|
||||
* Global Specify configuration. Should be set in bootstrap.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* // disable deep cloning of properties inside specify block
|
||||
* \Codeception\Specify\Config::setDeepClone(false);
|
||||
* ?>
|
||||
* ```
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
protected static $ignoredClasses = [
|
||||
'Codeception\Actor',
|
||||
'Symfony\Component\EventDispatcher\EventDispatcher',
|
||||
'Codeception\Scenario',
|
||||
'Codeception\Lib\Parser'
|
||||
];
|
||||
|
||||
protected static $ignoredProperties = [
|
||||
|
||||
// PHPUnit
|
||||
'backupGlobals',
|
||||
'backupGlobalsBlacklist',
|
||||
'backupStaticAttributes',
|
||||
'backupStaticAttributesBlacklist',
|
||||
'runTestInSeparateProcess',
|
||||
'preserveGlobalState',
|
||||
|
||||
// Codeception
|
||||
'dependencies',
|
||||
'dependencyInput',
|
||||
'tester',
|
||||
'guy',
|
||||
'name'
|
||||
];
|
||||
|
||||
protected static $deepClone = true;
|
||||
|
||||
public $is_deep = true;
|
||||
public $ignore = array();
|
||||
public $ignore_classes = array();
|
||||
public $shallow = array();
|
||||
public $deep = array();
|
||||
public $only = null;
|
||||
|
||||
public function propertyIgnored($property)
|
||||
{
|
||||
if ($this->only) {
|
||||
return !in_array($property, $this->only);
|
||||
}
|
||||
return in_array($property, $this->ignore);
|
||||
}
|
||||
|
||||
public function classIgnored($value)
|
||||
{
|
||||
if (!is_object($value)) return false;
|
||||
return in_array(get_class($value), $this->ignore_classes);
|
||||
}
|
||||
|
||||
public function propertyIsShallowCloned($property)
|
||||
{
|
||||
if ($this->only and !$this->is_deep) {
|
||||
return in_array($property, $this->only);
|
||||
}
|
||||
if (!$this->is_deep and !in_array($property, $this->deep)) {
|
||||
return true;
|
||||
}
|
||||
return in_array($property, $this->shallow);
|
||||
}
|
||||
|
||||
public function propertyIsDeeplyCloned($property)
|
||||
{
|
||||
if ($this->only and $this->is_deep) {
|
||||
return in_array($property, $this->only);
|
||||
}
|
||||
if ($this->is_deep and !in_array($property, $this->shallow)) {
|
||||
return true;
|
||||
}
|
||||
return in_array($property, $this->deep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable using of deep cloning for objects by default.
|
||||
* Deep cloning is the default.
|
||||
*
|
||||
* @param boolean $deepClone
|
||||
*/
|
||||
public static function setDeepClone($deepClone)
|
||||
{
|
||||
self::$deepClone = $deepClone;
|
||||
}
|
||||
|
||||
/***
|
||||
* Set classes which are going to be ignored for cloning in specify blocks.
|
||||
*
|
||||
* @param array $ignoredClasses
|
||||
*/
|
||||
public static function setIgnoredClasses($ignoredClasses)
|
||||
{
|
||||
self::$ignoredClasses = $ignoredClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Globally set class properties are going to be ignored for cloning in specify blocks.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* \Codeception\Specify\Config::setIgnoredProperties(['users', 'repository']);
|
||||
* ```
|
||||
*
|
||||
* @param array $ignoredProperties
|
||||
*/
|
||||
public static function setIgnoredProperties($ignoredProperties)
|
||||
{
|
||||
self::$ignoredProperties = $ignoredProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specific classes to cloning ignore list. Instances of those classes won't be cloned for specify blocks.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* \Codeception\Specify\Config::addIgnoredClasses(['\Acme\Domain\UserRepo', '\Acme\Domain\PostRepo']);
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param $ignoredClasses
|
||||
*/
|
||||
public static function addIgnoredClasses($ignoredClasses)
|
||||
{
|
||||
self::$ignoredClasses = array_merge(self::$ignoredClasses, $ignoredClasses);
|
||||
}
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
static function create()
|
||||
{
|
||||
$config = new Config();
|
||||
$config->is_deep = self::$deepClone;
|
||||
$config->ignore = self::$ignoredProperties;
|
||||
$config->ignore_classes = self::$ignoredClasses;
|
||||
$config->shallow = array();
|
||||
$config->deep = array();
|
||||
$config->only = null;
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
172
vendor/codeception/specify/src/Codeception/Specify/ConfigBuilder.php
vendored
Normal file
172
vendor/codeception/specify/src/Codeception/Specify/ConfigBuilder.php
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
namespace Codeception\Specify;
|
||||
|
||||
/**
|
||||
* Configure Specify usage.
|
||||
*
|
||||
* Specify copies properties of object and restores them for each specify block.
|
||||
* Objects can be cloned deeply or using standard `clone` operator.
|
||||
* Specify can be configured to prevent specific properties in specify blocks, to choose default cloning method,
|
||||
* or cloning method for specific properties.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->specifyConfig()
|
||||
* ->ignore('user') // do not clone
|
||||
* ?>
|
||||
* ```
|
||||
*/
|
||||
class ConfigBuilder
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
public function __construct(Config $config = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
if (!$config) {
|
||||
$this->config = Config::create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore cloning specific object properties in specify blocks.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->user = new User;
|
||||
* $this->specifyConfig()->ignore('user');
|
||||
* $this->specify('change user name', function() {
|
||||
* $this->user->name = 'davert';
|
||||
* });
|
||||
* $this->user->name == 'davert'; // name changed
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param array $properties
|
||||
* @return $this
|
||||
*/
|
||||
public function ignore($properties = array())
|
||||
{
|
||||
if (!is_array($properties)) {
|
||||
$properties = func_get_args();
|
||||
}
|
||||
$this->config->ignore = array_merge($this->config->ignore, $properties);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds specific class to ignore list, if property is an instance of class it will not be cloned for specify block.
|
||||
*
|
||||
* @param array $classes
|
||||
* @return $this
|
||||
*/
|
||||
public function ignoreClasses($classes = array())
|
||||
{
|
||||
$this->config->ignore_classes = array_merge($this->config->ignore_classes, $classes);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on/off deep cloning mode.
|
||||
* Deep cloning mode can also be specified for specific properties.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->user = new User;
|
||||
* $this->post = new Post;
|
||||
* $this->tag = new Tag;
|
||||
*
|
||||
* // turn on deep cloning by default
|
||||
* $this->specifyConfig()->deepClone();
|
||||
*
|
||||
* // turn off deep cloning by default
|
||||
* $this->specifyConfig()->deepClone(false);
|
||||
*
|
||||
* // deep clone only user and tag property
|
||||
* $this->specifyConfig()->deepClone('user', 'tag');
|
||||
*
|
||||
* // alternatively
|
||||
* $this->specifyConfig()->deepClone(['user', 'tag']);
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param bool $properties
|
||||
* @return $this
|
||||
*/
|
||||
public function deepClone($properties = true)
|
||||
{
|
||||
if (is_bool($properties)) {
|
||||
$this->config->is_deep = $properties;
|
||||
return $this;
|
||||
}
|
||||
if (!is_array($properties)) {
|
||||
$properties = func_get_args();
|
||||
}
|
||||
$this->config->deep = $properties;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable deep cloning mode, use shallow cloning by default, which is faster.
|
||||
* Deep cloning mode can also be disabled for specific properties.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->user = new User;
|
||||
* $this->post = new Post;
|
||||
* $this->tag = new Tag;
|
||||
*
|
||||
* // turn off deep cloning by default
|
||||
* $this->specifyConfig()->shallowClone();
|
||||
*
|
||||
* // turn on deep cloning by default
|
||||
* $this->specifyConfig()->shallowClone(false);
|
||||
*
|
||||
* // shallow clone only user and tag property
|
||||
* $this->specifyConfig()->shallowClone('user', 'tag');
|
||||
*
|
||||
* // alternatively
|
||||
* $this->specifyConfig()->shallowClone(['user', 'tag']);
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param bool $properties
|
||||
* @return $this
|
||||
*/
|
||||
public function shallowClone($properties = true)
|
||||
{
|
||||
if (is_bool($properties)) {
|
||||
$this->config->is_deep = !$properties;
|
||||
return $this;
|
||||
}
|
||||
if (!is_array($properties)) {
|
||||
$properties = func_get_args();
|
||||
}
|
||||
$this->config->shallow = array_merge($this->config->shallow, $properties);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone only specific properties
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* $this->specifyConfig()->cloneOnly('user', 'post');
|
||||
* ?>
|
||||
* ```
|
||||
*
|
||||
* @param $properties
|
||||
* @return $this
|
||||
*/
|
||||
public function cloneOnly($properties)
|
||||
{
|
||||
if (!is_array($properties)) {
|
||||
$properties = func_get_args();
|
||||
}
|
||||
$this->config->only = $properties;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
78
vendor/codeception/specify/src/Codeception/Specify/ObjectProperty.php
vendored
Normal file
78
vendor/codeception/specify/src/Codeception/Specify/ObjectProperty.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
namespace Codeception\Specify;
|
||||
|
||||
/**
|
||||
* Helper for manipulating by an object property.
|
||||
*
|
||||
* @author Roman Ishchenko <roman@ishchenko.ck.ua>
|
||||
*/
|
||||
class ObjectProperty
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $_owner;
|
||||
|
||||
/**
|
||||
* @var \ReflectionProperty|string
|
||||
*/
|
||||
private $_property;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $_initValue;
|
||||
|
||||
/**
|
||||
* ObjectProperty constructor.
|
||||
*
|
||||
* @param $owner
|
||||
* @param $property
|
||||
* @param $value
|
||||
*/
|
||||
public function __construct($owner, $property, $value = null)
|
||||
{
|
||||
$this->_owner = $owner;
|
||||
$this->_property = $property;
|
||||
|
||||
if (!($this->_property instanceof \ReflectionProperty)) {
|
||||
$this->_property = new \ReflectionProperty($owner, $this->_property);
|
||||
}
|
||||
|
||||
$this->_property->setAccessible(true);
|
||||
|
||||
$this->_initValue = ($value === null ? $this->getValue() : $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->_property->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores initial value
|
||||
*/
|
||||
public function restoreValue()
|
||||
{
|
||||
$this->setValue($this->_initValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->_property->getValue($this->_owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->_property->setValue($this->_owner, $value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user