diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..cca0805b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true + +[**.php] +indent_size = 4 +continuation_indent_size = 4 +insert_final_newline = true + +[**.xml] +indent_size = 4 + +[**.yml] +indent_size = 2 + +[composer.json] +indent_size = 4 + +[package.json] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 85dc9a8c..7325c690 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,8 @@ /test export-ignore /vendor export-ignore +.coveralls.yml export-ignore .gitattributes export-ignore .gitignore export-ignore .travis.yml export-ignore .php_cs export-ignore +phpunit.xml.dist export-ignore diff --git a/.gitignore b/.gitignore index 4cac0a21..a7fc91d6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ nbproject tmp/ clover.xml +composer.lock coveralls-upload.json phpunit.xml vendor diff --git a/.php_cs b/.php_cs index bf4b799f..569ffe52 100644 --- a/.php_cs +++ b/.php_cs @@ -1,5 +1,7 @@ in('src') + ->in('test') ->notPath('TestAsset') ->notPath('_files') ->filter(function (SplFileInfo $file) { @@ -28,9 +30,10 @@ $config->fixers( 'multiple_use', 'method_argument_space', 'object_operator', + 'ordered_use', 'php_closing_tag', - 'psr0', 'remove_lines_between_uses', + 'long_array_syntax', 'short_tag', 'standardize_not_equal', 'trailing_spaces', diff --git a/.travis.yml b/.travis.yml index fe909ecb..989ddc78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,16 +2,28 @@ sudo: false language: php +branches: + except: + - /^release-.*$/ + - /^ghgfk-.*$/ + +cache: + directories: + - $HOME/.composer/cache + matrix: fast_finish: true include: + - php: 5.3 + - php: 5.4 - php: 5.5 + env: + - EXECUTE_CS_CHECK=true - php: 5.6 env: - EXECUTE_TEST_COVERALLS=true - - EXECUTE_CS_CHECK=true - php: 7 - - php: hhvm + - php: hhvm allow_failures: - php: 7 - php: hhvm @@ -22,14 +34,16 @@ notifications: before_install: - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi + - composer self-update + - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then composer require --dev --no-update satooshi/php-coveralls ; fi install: - - composer install --no-interaction --prefer-source + - travis_retry composer install --no-interaction --ignore-platform-reqs script: - - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit -c phpunit.xml.travis --coverage-clover clover.xml ; fi - - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then ./vendor/bin/phpunit -c phpunit.xml.travis ; fi - - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run --config-file=.php_cs ; fi + - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; fi + - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then ./vendor/bin/phpunit ; fi + - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run ; fi after_script: - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/coveralls ; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index fca9f311..cf3e8fe6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,67 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.4.8 - TBD + +### Added + +- Nothing. + +### Deprecated + +- [#26](https://github.com/zendframework/zend-inputfilter/pull/26) Deprecate magic logic for auto attach a NonEmpty + validator with breakChainOnFailure = true. Instead append NonEmpty validator when desired. + + ```php + $input = new Zend\InputFilter\Input(); + $input->setContinueIfEmpty(true); + $input->setAllowEmpty(true); + $input->getValidatorChain()->attach(new Zend\Validator\NotEmpty(), /* break chain on failure */ true); + ``` +### Removed + +- Nothing. + +### Fixed + +- [#22](https://github.com/zendframework/zend-inputfilter/pull/22) adds tests to + verify two conditions around inputs with fallback values: + - If the input was not in the data set, it should not be represented in either + the list of valid *or* invalid inputs. + - If the input *was* in the data set, but empty, it should be represented in + the list of valid inputs. +- [#31](https://github.com/zendframework/zend-inputfilter/pull/31) updates the + `InputFilterInterface::add()` docblock to match existing, shipped implementations. +- [#25](https://github.com/zendframework/zend-inputfilter/pull/25) Fix missing optional fields to be required. + BC Break since 2.3.9. + For completely fix this you need to setup your inputs as follow. + + ```php + $input = new Input(); + $input->setAllowEmpty(true); // Disable BC Break logic related to treat `null` values as valid empty value instead *not set*. + $input->setContinueIfEmpty(true); // Disable BC Break logic related to treat `null` values as valid empty value instead *not set*. + $input->getValidatorChain()->attach( + new Zend\Validator\NotEmpty(), + true // break chain on failure + ); + ``` + + ```php + $inputSpecification = array( + 'allow_empty' => true, + 'continue_if_empty' => true, + 'validators' => array( + array( + 'break_chain_on_failure' => true, + 'name' => 'Zend\\Validator\\NotEmpty', + ), + ), + ); + ``` +- [Numerous fixes](https://github.com/zendframework/zend-inputfilter/milestones/2.4.8) + aimed at bringing the functionality back to the pre-2.4 code, and improving + quality overall of the component via increased testing and test coverage. + ## 2.4.7 - 2015-08-11 ### Added diff --git a/README.md b/README.md index 68d7faff..13861a74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # zend-inputfilter +[![Build Status](https://secure.travis-ci.org/zendframework/zend-inputfilter.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-inputfilter) +[![Coverage Status](https://coveralls.io/repos/zendframework/zend-inputfilter/badge.svg?branch=master)](https://coveralls.io/r/zendframework/zend-inputfilter?branch=master) + The `Zend\InputFilter` component can be used to filter and validate generic sets of input data. For instance, you could use it to filter `$_GET` or `$_POST` values, CLI arguments, etc. diff --git a/composer.json b/composer.json index b618c816..e6bac4ce 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "zf2", "inputfilter" ], - "homepage": "https://github.com/zendframework/zend-input-filter", + "homepage": "https://github.com/zendframework/zend-inputfilter", "autoload": { "psr-4": { "Zend\\InputFilter\\": "src/" @@ -14,19 +14,20 @@ }, "require": { "php": ">=5.3.23", - "zendframework/zend-filter": "self.version", - "zendframework/zend-validator": "self.version", - "zendframework/zend-stdlib": "self.version" + "zendframework/zend-filter": "~2.4.0", + "zendframework/zend-validator": "~2.4.8", + "zendframework/zend-stdlib": "~2.4.0" }, "require-dev": { - "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-servicemanager": "~2.4.0", "fabpot/php-cs-fixer": "1.7.*", - "satooshi/php-coveralls": "dev-master", - "phpunit/PHPUnit": "~4.0" + "phpunit/PHPUnit": "^4.5" }, "suggest": { "zendframework/zend-servicemanager": "To support plugin manager support" }, + "minimum-stability": "dev", + "prefer-stable": true, "extra": { "branch-alias": { "dev-master": "2.4-dev", @@ -38,4 +39,4 @@ "ZendTest\\InputFilter\\": "test/" } } -} \ No newline at end of file +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ee25b409..493e8228 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -28,7 +28,7 @@ functionality works as expected. Such tests include those for Zend\Soap and Zend\Session, which require that headers not be sent in order to work. --> - + diff --git a/phpunit.xml.travis b/phpunit.xml.travis deleted file mode 100644 index ee25b409..00000000 --- a/phpunit.xml.travis +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ./test/ - - - - - - disable - - - - - - ./src - - - - - - - - - - - diff --git a/src/ArrayInput.php b/src/ArrayInput.php index 08876f91..102d6fb1 100644 --- a/src/ArrayInput.php +++ b/src/ArrayInput.php @@ -31,6 +31,16 @@ public function setValue($value) return parent::setValue($value); } + /** + * {@inheritdoc} + */ + public function resetValue() + { + $this->value = array(); + $this->hasValue = false; + return $this; + } + /** * @return array */ @@ -50,6 +60,22 @@ public function getValue() */ public function isValid($context = null) { + $hasValue = $this->hasValue(); + $required = $this->isRequired(); + $hasFallback = $this->hasFallback(); + + if (! $hasValue && $hasFallback) { + $this->setValue($this->getFallbackValue()); + return true; + } + + if (! $hasValue && $required) { + if ($this->errorMessage === null) { + $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); + } + return false; + } + if (!$this->continueIfEmpty() && !$this->allowEmpty()) { $this->injectNotEmptyValidator(); } @@ -58,15 +84,19 @@ public function isValid($context = null) $result = true; foreach ($values as $value) { $empty = ($value === null || $value === '' || $value === array()); + if ($empty && !$this->isRequired() && !$this->continueIfEmpty()) { + $result = true; + continue; + } if ($empty && $this->allowEmpty() && !$this->continueIfEmpty()) { $result = true; continue; } $result = $validator->isValid($value, $context); if (!$result) { - if ($this->hasFallback()) { + if ($hasFallback) { $this->setValue($this->getFallbackValue()); - $result = true; + return true; } break; } diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index 74e69859..0b2d1bd8 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -21,7 +21,7 @@ class BaseInputFilter implements ReplaceableInputInterface { /** - * @var array + * @var null|array */ protected $data; @@ -36,7 +36,7 @@ class BaseInputFilter implements protected $invalidInputs; /** - * @var array + * @var null|string[] Input names */ protected $validationGroup; @@ -106,23 +106,13 @@ public function add($input, $name = null) /** * Replace a named input * - * @param InputInterface|InputFilterInterface $input + * @param mixed $input Any of the input types allowed on add() method. * @param string $name Name of the input to replace - * @throws Exception\InvalidArgumentException + * @throws Exception\InvalidArgumentException If input to replace not exists. * @return self */ public function replace($input, $name) { - if (!$input instanceof InputInterface && !$input instanceof InputFilterInterface) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects an instance of %s or %s as its first argument; received "%s"', - __METHOD__, - 'Zend\InputFilter\InputInterface', - 'Zend\InputFilter\InputFilterInterface', - (is_object($input) ? get_class($input) : gettype($input)) - )); - } - if (!array_key_exists($name, $this->inputs)) { throw new Exception\InvalidArgumentException(sprintf( '%s: no input found matching "%s"', @@ -131,7 +121,9 @@ public function replace($input, $name) )); } - $this->inputs[$name] = $input; + $this->remove($name); + $this->add($input, $name); + return $this; } @@ -186,16 +178,16 @@ public function remove($name) */ public function setData($data) { - if (!is_array($data) && !$data instanceof Traversable) { + if ($data instanceof Traversable) { + $data = ArrayUtils::iteratorToArray($data); + } + if (!is_array($data)) { throw new Exception\InvalidArgumentException(sprintf( '%s expects an array or Traversable argument; received %s', __METHOD__, (is_object($data) ? get_class($data) : gettype($data)) )); } - if (is_object($data) && !$data instanceof ArrayAccess) { - $data = ArrayUtils::iteratorToArray($data); - } $this->data = $data; $this->populate(); return $this; @@ -224,17 +216,14 @@ public function isValid($context = null) /** * Validate a set of inputs against the current data * - * @param array $inputs + * @param string[] $inputs Array of input names. * @param array|ArrayAccess $data * @param mixed|null $context * @return bool */ protected function validateInputs(array $inputs, $data = array(), $context = null) { - // backwards compatibility - if (empty($data)) { - $data = $this->getRawValues(); - } + $inputContext = $context ?: (array_merge($this->getRawValues(), (array) $data)); $this->validInputs = array(); $this->invalidInputs = array(); @@ -242,42 +231,6 @@ protected function validateInputs(array $inputs, $data = array(), $context = nul foreach ($inputs as $name) { $input = $this->inputs[$name]; - $hasFallback = ($input instanceof Input && $input->hasFallback()); - - // If the value is required, not present in the data set, and - // has no fallback, validation fails. - if (!array_key_exists($name, $data) - && $input instanceof InputInterface - && $input->isRequired() - && !$hasFallback - ) { - $input->setErrorMessage('Value is required'); - $this->invalidInputs[$name] = $input; - - if ($input->breakOnFailure()) { - return false; - } - - $valid = false; - continue; - } - - // If the value is required, not present in the data set, and - // has a fallback, validation passes, and we set the input - // value to the fallback. - if (!array_key_exists($name, $data) - && $input instanceof InputInterface - && $input->isRequired() - && $hasFallback - ) { - $input->setValue($input->getFallbackValue()); - continue; - } - - // Make sure we have a value (empty) for validation of context - if (!array_key_exists($name, $data)) { - $data[$name] = null; - } // Validate an input filter if ($input instanceof InputFilterInterface) { @@ -290,23 +243,30 @@ protected function validateInputs(array $inputs, $data = array(), $context = nul continue; } - // Validate an input - if ($input instanceof InputInterface) { - $inputContext = $context ?: $data; + // If input is not InputInterface then silently continue (BC safe) + if (!$input instanceof InputInterface) { + continue; + } - if (!$input->isValid($inputContext)) { - // Validation failure - $this->invalidInputs[$name] = $input; - $valid = false; + // If input is optional (not required), and value is not set, then ignore. + if (!array_key_exists($name, $data) + && !$input->isRequired() + ) { + continue; + } - if ($input->breakOnFailure()) { - return false; - } - continue; + // Validate an input + if (!$input->isValid($inputContext)) { + // Validation failure + $this->invalidInputs[$name] = $input; + $valid = false; + + if ($input->breakOnFailure()) { + return false; } - $this->validInputs[$name] = $input; continue; } + $this->validInputs[$name] = $input; } return $valid; @@ -465,6 +425,9 @@ public function getRawValue($name) )); } $input = $this->inputs[$name]; + if ($input instanceof InputFilterInterface) { + return $input->getRawValues(); + } return $input->getRawValue(); } @@ -511,7 +474,7 @@ public function getMessages() /** * Ensure all names of a validation group exist as input in the filter * - * @param array $inputs + * @param string[] $inputs Input names * @return void * @throws Exception\InvalidArgumentException */ @@ -542,7 +505,7 @@ protected function populate() $input->clearRawValues(); } - if (!isset($this->data[$name])) { + if (!array_key_exists($name, $this->data)) { // No value; clear value in this input if ($input instanceof InputFilterInterface) { $input->setData(array()); @@ -554,6 +517,11 @@ protected function populate() continue; } + if ($input instanceof Input) { + $input->resetValue(); + continue; + } + $input->setValue(null); continue; } @@ -627,7 +595,7 @@ public function getUnknown() /** * Get an array of all inputs * - * @return array + * @return InputInterface[]|InputFilterInterface[] */ public function getInputs() { diff --git a/src/CollectionInputFilter.php b/src/CollectionInputFilter.php index c4cc348a..6d34c7c1 100644 --- a/src/CollectionInputFilter.php +++ b/src/CollectionInputFilter.php @@ -13,27 +13,27 @@ class CollectionInputFilter extends InputFilter { - /* + /** * @var bool */ protected $isRequired = false; - /* + /** * @var int */ protected $count = null; - /* - * @var array + /** + * @var array[] */ protected $collectionValues = array(); - /* - * @var array + /** + * @var array[] */ protected $collectionRawValues = array(); - /* + /** * @var array */ protected $collectionMessages = array(); @@ -140,6 +140,8 @@ public function getCount() public function setData($data) { $this->data = $data; + + return $this; } /** @@ -226,7 +228,7 @@ public function getRawValues() /** * Clear collectionValues * - * @access public + * @return array[] */ public function clearValues() { @@ -236,7 +238,7 @@ public function clearValues() /** * Clear collectionRawValues * - * @access public + * @return array[] */ public function clearRawValues() { diff --git a/src/EmptyContextInterface.php b/src/EmptyContextInterface.php index 62e2e225..fd049db1 100644 --- a/src/EmptyContextInterface.php +++ b/src/EmptyContextInterface.php @@ -9,15 +9,22 @@ namespace Zend\InputFilter; +/** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + */ interface EmptyContextInterface { /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. + * * @param bool $continueIfEmpty * @return self */ public function setContinueIfEmpty($continueIfEmpty); /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. Should always return `true`. + * * @return bool */ public function continueIfEmpty(); diff --git a/src/Factory.php b/src/Factory.php index 244233ec..d07ecc6b 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -10,12 +10,11 @@ namespace Zend\InputFilter; use Traversable; -use Zend\Filter\Exception; use Zend\Filter\FilterChain; +use Zend\ServiceManager\ServiceLocatorInterface; use Zend\Stdlib\ArrayUtils; -use Zend\Validator\ValidatorInterface; use Zend\Validator\ValidatorChain; -use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\Validator\ValidatorInterface; class Factory { @@ -171,19 +170,20 @@ public function createInput($inputSpecification) if (isset($inputSpecification['type'])) { $class = $inputSpecification['type']; + } - if ($this->getInputFilterManager()->has($class)) { - return $this->createInputFilter($inputSpecification); - } - - if (!class_exists($class)) { - throw new Exception\RuntimeException(sprintf( - 'Input factory expects the "type" to be a valid class; received "%s"', - $class - )); - } + $managerInstance = null; + if ($this->getInputFilterManager()->has($class)) { + $managerInstance = $this->getInputFilterManager()->get($class); + } + if (! $managerInstance && ! class_exists($class)) { + throw new Exception\RuntimeException(sprintf( + 'Input factory expects the "type" to be a valid class or a plugin name; received "%s"', + $class + )); } - $input = new $class(); + + $input = $managerInstance ?: new $class; if ($input instanceof InputFilterInterface) { return $this->createInputFilter($inputSpecification); @@ -211,9 +211,6 @@ public function createInput($inputSpecification) break; case 'required': $input->setRequired($value); - if (isset($inputSpecification['allow_empty'])) { - $input->setAllowEmpty($inputSpecification['allow_empty']); - } break; case 'allow_empty': $input->setAllowEmpty($value); @@ -222,12 +219,26 @@ public function createInput($inputSpecification) } break; case 'continue_if_empty': + if (!$input instanceof Input) { + throw new Exception\RuntimeException(sprintf( + '%s "continue_if_empty" can only set to inputs of type "%s"', + __METHOD__, + 'Zend\InputFilter\Input' + )); + } $input->setContinueIfEmpty($inputSpecification['continue_if_empty']); break; case 'error_message': $input->setErrorMessage($value); break; case 'fallback_value': + if (!$input instanceof Input) { + throw new Exception\RuntimeException(sprintf( + '%s "fallback_value" can only set to inputs of type "%s"', + __METHOD__, + 'Zend\InputFilter\Input' + )); + } $input->setFallbackValue($value); break; case 'break_on_failure': @@ -375,7 +386,7 @@ protected function populateFilters(FilterChain $chain, $filters) /** * @param ValidatorChain $chain - * @param array|Traversable $validators + * @param string[]|ValidatorInterface[] $validators * @throws Exception\RuntimeException * @return void */ diff --git a/src/FileInput.php b/src/FileInput.php index 3e0ac77d..ed59091a 100644 --- a/src/FileInput.php +++ b/src/FileInput.php @@ -113,16 +113,28 @@ public function isEmptyFile($rawValue) public function isValid($context = null) { $rawValue = $this->getRawValue(); + $hasValue = $this->hasValue(); $empty = $this->isEmptyFile($rawValue); $required = $this->isRequired(); $allowEmpty = $this->allowEmpty(); $continueIfEmpty = $this->continueIfEmpty(); + if (! $hasValue && ! $required) { + return true; + } + + if (! $hasValue && $required && ! $this->hasFallback()) { + if ($this->errorMessage === null) { + $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); + } + return false; + } + if ($empty && ! $required && ! $continueIfEmpty) { return true; } - if ($empty && $required && $allowEmpty && ! $continueIfEmpty) { + if ($empty && $allowEmpty && ! $continueIfEmpty) { return true; } @@ -181,6 +193,8 @@ protected function injectUploadValidator() } /** + * @deprecated 2.4.8 See note on parent class. Removal does not affect this class. + * * No-op, NotEmpty validator does not apply for FileInputs. * See also: BaseInputFilter::isValid() * diff --git a/src/Input.php b/src/Input.php index fe2395bc..cf51ba02 100644 --- a/src/Input.php +++ b/src/Input.php @@ -18,11 +18,15 @@ class Input implements EmptyContextInterface { /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @var bool */ protected $allowEmpty = false; /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @var bool */ protected $continueIfEmpty = false; @@ -48,6 +52,8 @@ class Input implements protected $name; /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @var bool */ protected $notEmptyValidator = false; @@ -67,6 +73,13 @@ class Input implements */ protected $value; + /** + * Flag for distinguish when $value contains the value previously set or the default one. + * + * @var bool + */ + protected $hasValue = false; + /** * @var mixed */ @@ -83,6 +96,8 @@ public function __construct($name = null) } /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. + * * @param bool $allowEmpty * @return Input */ @@ -103,6 +118,8 @@ public function setBreakOnFailure($breakOnFailure) } /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. + * * @param bool $continueIfEmpty * @return Input */ @@ -163,12 +180,36 @@ public function setValidatorChain(ValidatorChain $validatorChain) } /** + * Set the input value. + * + * If you want to remove/unset the current value use {@link Input::resetValue()}. + * + * @see Input::getValue() For retrieve the input value. + * @see Input::hasValue() For to know if input value was set. + * @see Input::resetValue() For reset the input value to the default state. + * * @param mixed $value * @return Input */ public function setValue($value) { $this->value = $value; + $this->hasValue = true; + return $this; + } + + /** + * Reset input value to the default state. + * + * @see Input::hasValue() For to know if input value was set. + * @see Input::setValue() For set a new value. + * + * @return Input + */ + public function resetValue() + { + $this->value = null; + $this->hasValue = false; return $this; } @@ -184,6 +225,8 @@ public function setFallbackValue($value) } /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @return bool */ public function allowEmpty() @@ -200,6 +243,8 @@ public function breakOnFailure() } /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. Should always return `true`. + * * @return bool */ public function continueIfEmpty() @@ -270,6 +315,22 @@ public function getValue() return $filter->filter($this->value); } + /** + * Flag for inform if input value was set. + * + * This flag used for distinguish when {@link Input::getValue()} will return the value previously set or the default. + * + * @see Input::getValue() For retrieve the input value. + * @see Input::setValue() For set a new value. + * @see Input::resetValue() For reset the input value to the default state. + * + * @return bool + */ + public function hasValue() + { + return $this->hasValue; + } + /** * @return mixed */ @@ -299,12 +360,16 @@ public function clearFallbackValue() public function merge(InputInterface $input) { $this->setBreakOnFailure($input->breakOnFailure()); - $this->setContinueIfEmpty($input->continueIfEmpty()); + if ($input instanceof Input) { + $this->setContinueIfEmpty($input->continueIfEmpty()); + } $this->setErrorMessage($input->getErrorMessage()); $this->setName($input->getName()); $this->setRequired($input->isRequired()); $this->setAllowEmpty($input->allowEmpty()); - $this->setValue($input->getRawValue()); + if (!($input instanceof Input) || $input->hasValue()) { + $this->setValue($input->getRawValue()); + } $filterChain = $input->getFilterChain(); $this->getFilterChain()->merge($filterChain); @@ -321,16 +386,33 @@ public function merge(InputInterface $input) public function isValid($context = null) { $value = $this->getValue(); + $hasValue = $this->hasValue(); $empty = ($value === null || $value === '' || $value === array()); $required = $this->isRequired(); $allowEmpty = $this->allowEmpty(); $continueIfEmpty = $this->continueIfEmpty(); + if (! $hasValue && $this->hasFallback()) { + $this->setValue($this->getFallbackValue()); + return true; + } + + if (! $hasValue && ! $required) { + return true; + } + + if (! $hasValue && $required) { + if ($this->errorMessage === null) { + $this->errorMessage = $this->prepareRequiredValidationFailureMessage(); + } + return false; + } + if ($empty && ! $required && ! $continueIfEmpty) { return true; } - if ($empty && $required && $allowEmpty && ! $continueIfEmpty) { + if ($empty && $allowEmpty && ! $continueIfEmpty) { return true; } @@ -353,7 +435,7 @@ public function isValid($context = null) } /** - * @return array + * @return string[] */ public function getMessages() { @@ -370,6 +452,8 @@ public function getMessages() } /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @return void */ protected function injectNotEmptyValidator() @@ -398,4 +482,18 @@ protected function injectNotEmptyValidator() $chain->prependValidator(new NotEmpty(), true); } + + /** + * Create and return the validation failure message for required input. + * + * @return string[] + */ + protected function prepareRequiredValidationFailureMessage() + { + $notEmpty = new NotEmpty(); + $templates = $notEmpty->getOption('messageTemplates'); + return array( + NotEmpty::IS_EMPTY => $templates[NotEmpty::IS_EMPTY], + ); + } } diff --git a/src/InputFilterAbstractServiceFactory.php b/src/InputFilterAbstractServiceFactory.php index 19a53b7f..5d8c5506 100644 --- a/src/InputFilterAbstractServiceFactory.php +++ b/src/InputFilterAbstractServiceFactory.php @@ -50,7 +50,7 @@ public function canCreateServiceWithName(ServiceLocatorInterface $inputFilters, * @param ServiceLocatorInterface $inputFilters * @param string $cName * @param string $rName - * @return \Zend\InputFilter\InputFilterInterface + * @return InputFilterInterface */ public function createServiceWithName(ServiceLocatorInterface $inputFilters, $cName, $rName) { diff --git a/src/InputFilterInterface.php b/src/InputFilterInterface.php index d1df0615..40550169 100644 --- a/src/InputFilterInterface.php +++ b/src/InputFilterInterface.php @@ -19,9 +19,12 @@ interface InputFilterInterface extends Countable /** * Add an input to the input filter * - * @param InputInterface|InputFilterInterface|array $input + * @param InputInterface|InputFilterInterface|array|Traversable $input + * Implementations MUST handle at least one of the specified types, and + * raise an exception for any they cannot process. * @param null|string $name Name used to retrieve this input * @return InputFilterInterface + * @throws Exception\InvalidArgumentException if unable to handle the input type. */ public function add($input, $name = null); @@ -142,7 +145,7 @@ public function getRawValues(); * Should return an associative array of named input/message list pairs. * Pairs should only be returned for inputs that failed validation. * - * @return array + * @return string[] */ public function getMessages(); } diff --git a/src/InputFilterPluginManager.php b/src/InputFilterPluginManager.php index bb5bccc3..c7460588 100644 --- a/src/InputFilterPluginManager.php +++ b/src/InputFilterPluginManager.php @@ -24,7 +24,7 @@ class InputFilterPluginManager extends AbstractPluginManager /** * Default set of plugins * - * @var array + * @var string[] */ protected $invokableClasses = array( 'inputfilter' => 'Zend\InputFilter\InputFilter', @@ -83,8 +83,10 @@ public function validatePlugin($plugin) } throw new Exception\RuntimeException(sprintf( - 'Plugin of type %s is invalid; must implement Zend\InputFilter\InputFilterInterface', - (is_object($plugin) ? get_class($plugin) : gettype($plugin)) + 'Plugin of type %s is invalid; must implement %s or %s', + (is_object($plugin) ? get_class($plugin) : gettype($plugin)), + 'Zend\InputFilter\InputFilterInterface', + 'Zend\InputFilter\InputInterface' )); } } diff --git a/src/InputInterface.php b/src/InputInterface.php index 14b0529a..cde375fa 100644 --- a/src/InputInterface.php +++ b/src/InputInterface.php @@ -15,6 +15,8 @@ interface InputInterface { /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain and set this to `true`. + * * @param bool $allowEmpty * @return self */ @@ -69,6 +71,8 @@ public function setValue($value); public function merge(InputInterface $input); /** + * @deprecated 2.4.8 Add Zend\Validator\NotEmpty validator to the ValidatorChain. + * * @return bool */ public function allowEmpty(); @@ -119,7 +123,7 @@ public function getValue(); public function isValid(); /** - * @return array + * @return string[] */ public function getMessages(); } diff --git a/test/ArrayInputTest.php b/test/ArrayInputTest.php index 7268d677..84e24343 100644 --- a/test/ArrayInputTest.php +++ b/test/ArrayInputTest.php @@ -10,9 +10,10 @@ namespace ZendTest\InputFilter; use Zend\InputFilter\ArrayInput; -use Zend\Filter; -use Zend\Validator; +/** + * @covers Zend\InputFilter\ArrayInput + */ class ArrayInputTest extends InputTest { public function setUp() @@ -20,230 +21,88 @@ public function setUp() $this->input = new ArrayInput('foo'); } - public function testValueIsNullByDefault() - { - $this->markTestSkipped('Test is not enabled in ArrayInputTest'); - } - - public function testValueIsEmptyArrayByDefault() + public function testDefaultGetValue() { $this->assertCount(0, $this->input->getValue()); } - public function testNotArrayValueCannotBeInjected() + public function testSetValueWithInvalidInputTypeThrowsInvalidArgumentException() { - $this->setExpectedException('Zend\InputFilter\Exception\InvalidArgumentException'); + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'Value must be an array, string given' + ); $this->input->setValue('bar'); } - public function testValueMayBeInjected() + public function fallbackValueVsIsValidProvider() { - $this->input->setValue(array('bar')); - $this->assertEquals(array('bar'), $this->input->getValue()); - } + $dataSets = parent::fallbackValueVsIsValidProvider(); + array_walk($dataSets, function (&$set) { + $set[1] = array($set[1]); // Wrap fallback value into an array. + $set[2] = array($set[2]); // Wrap value into an array. + $set[4] = array($set[4]); // Wrap expected value into an array. + }); - public function testRetrievingValueFiltersTheValue() - { - $this->input->setValue(array('bar')); - $filter = new Filter\StringToUpper(); - $this->input->getFilterChain()->attach($filter); - $this->assertEquals(array('BAR'), $this->input->getValue()); - } - - public function testCanRetrieveRawValue() - { - $this->input->setValue(array('bar')); - $filter = new Filter\StringToUpper(); - $this->input->getFilterChain()->attach($filter); - $this->assertEquals(array('bar'), $this->input->getRawValue()); - } - - public function testIsValidReturnsFalseIfValidationChainFails() - { - $this->input->setValue(array('123', 'bar')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - } - - public function testIsValidReturnsTrueIfValidationChainSucceeds() - { - $this->input->setValue(array('123', '123')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); + return $dataSets; } - public function testValidationOperatesOnFilteredValue() + public function emptyValueProvider() { - $this->input->setValue(array(' 123 ', ' 123')); - $filter = new Filter\StringTrim(); - $this->input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); - } + $dataSets = parent::emptyValueProvider(); + array_walk($dataSets, function (&$set) { + $set['raw'] = array($set['raw']); // Wrap value into an array. + }); - public function testGetMessagesReturnsValidationMessages() - { - $this->input->setValue(array('bar')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $messages); + return $dataSets; } - public function testSpecifyingMessagesToInputReturnsThoseOnFailedValidation() + public function mixedValueProvider() { - $this->input->setValue(array('bar')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->input->setErrorMessage('Please enter only digits'); - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayNotHasKey(Validator\Digits::NOT_DIGITS, $messages); - $this->assertContains('Please enter only digits', $messages); - } + $dataSets = parent::mixedValueProvider(); + array_walk($dataSets, function (&$set) { + $set['raw'] = array($set['raw']); // Wrap value into an array. + }); - public function testNotEmptyValidatorAddedWhenIsValidIsCalled() - { - $this->assertTrue($this->input->isRequired()); - $this->input->setValue(array('bar', '')); - $validatorChain = $this->input->getValidatorChain(); - $this->assertEquals(0, count($validatorChain->getValidators())); - - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayHasKey('isEmpty', $messages); - $this->assertEquals(1, count($validatorChain->getValidators())); - - // Assert that NotEmpty validator wasn't added again - $this->assertFalse($this->input->isValid()); - $this->assertEquals(1, count($validatorChain->getValidators())); + return $dataSets; } - public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() + public function createFilterChainMock($valueRaw = null, $valueFiltered = null) { - $this->assertTrue($this->input->isRequired()); - $this->input->setValue(array('bar', '')); - - $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); - $notEmptyMock->expects($this->exactly(1)) - ->method('isValid') - ->will($this->returnValue(false)); + // ArrayInput filters per each array value + if (is_array($valueRaw)) { + $valueRaw = current($valueRaw); + } - $validatorChain = $this->input->getValidatorChain(); - $validatorChain->prependValidator($notEmptyMock); - $this->assertFalse($this->input->isValid()); + if (is_array($valueFiltered)) { + $valueFiltered = current($valueFiltered); + } - $validators = $validatorChain->getValidators(); - $this->assertEquals(1, count($validators)); - $this->assertEquals($notEmptyMock, $validators[0]['instance']); + return parent::createFilterChainMock($valueRaw, $valueFiltered); } - public function testMerge() + public function createValidatorChainMock($isValid = null, $value = null, $context = null, $messages = array()) { - $input = new ArrayInput('foo'); - $input->setValue(array(' 123 ')); - $filter = new Filter\StringTrim(); - $input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $input->getValidatorChain()->attach($validator); - - $input2 = new ArrayInput('bar'); - $input2->merge($input); - $validatorChain = $input->getValidatorChain(); - $filterChain = $input->getFilterChain(); - - $this->assertEquals(array(' 123 '), $input2->getRawValue()); - $this->assertEquals(1, $validatorChain->count()); - $this->assertEquals(1, $filterChain->count()); - - $validators = $validatorChain->getValidators(); - $this->assertInstanceOf('Zend\Validator\Digits', $validators[0]['instance']); - - $filters = $filterChain->getFilters()->toArray(); - $this->assertInstanceOf('Zend\Filter\StringTrim', $filters[0]); - } + // ArrayInput validates per each array value + if (is_array($value)) { + $value = current($value); + } - public function testDoNotInjectNotEmptyValidatorIfAnywhereInChain() - { - $this->assertTrue($this->input->isRequired()); - $this->input->setValue(array('bar', '')); - - $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); - $notEmptyMock->expects($this->exactly(1)) - ->method('isValid') - ->will($this->returnValue(false)); - - $validatorChain = $this->input->getValidatorChain(); - $validatorChain->attach(new Validator\Digits()); - $validatorChain->attach($notEmptyMock); - $this->assertFalse($this->input->isValid()); - - $validators = $validatorChain->getValidators(); - $this->assertEquals(2, count($validators)); - $this->assertEquals($notEmptyMock, $validators[1]['instance']); + return parent::createValidatorChainMock($isValid, $value, $context, $messages); } - public function dataFallbackValue() + public function createNonEmptyValidatorMock($isValid, $value, $context = null) { - return array( - array( - 'fallbackValue' => array() - ), - array( - 'fallbackValue' => array(''), - ), - array( - 'fallbackValue' => array(null), - ), - array( - 'fallbackValue' => array('some value'), - ), - ); - } + // ArrayInput validates per each array value + if (is_array($value)) { + $value = current($value); + } - /** - * @dataProvider dataFallbackValue - */ - public function testFallbackValue($fallbackValue) - { - $this->input->setFallbackValue($fallbackValue); - $validator = new Validator\Date(); - $this->input->getValidatorChain()->attach($validator); - $this->input->setValue(array('123')); // not a date - - $this->assertTrue($this->input->isValid()); - $this->assertEmpty($this->input->getMessages()); - $this->assertSame($fallbackValue, $this->input->getValue()); - } - - public function emptyValuesProvider() - { - return array( - array(array(null)), - array(array('')), - array(array(array())), - ); - } - - public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() - { - $this->input->setValue(array('nonempty')) - ->getFilterChain()->attach(new Filter\Callback(function () { - return ''; - })); - $this->assertFalse($this->input->isValid()); + return parent::createNonEmptyValidatorMock($isValid, $value, $context); } - public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() + public function getDummyValue($raw = true) { - $this->input->setValue(array('')) - ->getFilterChain()->attach(new Filter\Callback(function () { - return 'nonempty'; - })); - $this->assertTrue($this->input->isValid()); + return array(parent::getDummyValue($raw)); } } diff --git a/test/BaseInputFilterTest.php b/test/BaseInputFilterTest.php index 3a8664d1..d11eb890 100644 --- a/test/BaseInputFilterTest.php +++ b/test/BaseInputFilterTest.php @@ -9,815 +9,519 @@ namespace ZendTest\InputFilter; +use ArrayIterator; use ArrayObject; +use FilterIterator; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_TestCase as TestCase; use stdClass; +use Zend\InputFilter\ArrayInput; +use Zend\InputFilter\BaseInputFilter; use Zend\InputFilter\Input; -use Zend\InputFilter\FileInput; -use Zend\InputFilter\BaseInputFilter as InputFilter; -use Zend\Filter; -use Zend\Validator; +use Zend\InputFilter\InputFilterInterface; +use Zend\InputFilter\InputInterface; +/** + * @covers Zend\InputFilter\BaseInputFilter + */ class BaseInputFilterTest extends TestCase { - public function testInputFilterIsEmptyByDefault() - { - $filter = new InputFilter(); - $this->assertEquals(0, count($filter)); - } + /** + * @var BaseInputFilter + */ + protected $inputFilter; - public function testAddingInputsIncreasesCountOfFilter() + public function setUp() { - $filter = new InputFilter(); - $foo = new Input('foo'); - $filter->add($foo); - $this->assertEquals(1, count($filter)); - $bar = new Input('bar'); - $filter->add($bar); - $this->assertEquals(2, count($filter)); + $this->inputFilter = new BaseInputFilter(); } - public function testAddingInputWithNameDoesNotInjectNameInInput() + public function testInputFilterIsEmptyByDefault() { - $filter = new InputFilter(); - $foo = new Input('foo'); - $filter->add($foo, 'bar'); - $test = $filter->get('bar'); - $this->assertSame($foo, $test); - $this->assertEquals('foo', $foo->getName()); + $filter = $this->inputFilter; + $this->assertEquals(0, count($filter)); } - public function testCanAddInputFilterAsInput() + public function testAddWithInvalidInputTypeThrowsInvalidArgumentException() { - $parent = new InputFilter(); - $child = new InputFilter(); - $parent->add($child, 'child'); - $this->assertEquals(1, count($parent)); - $this->assertSame($child, $parent->get('child')); - } + $inputFilter = $this->inputFilter; - public function testCanRemoveInputFilter() - { - $parent = new InputFilter(); - $child = new InputFilter(); - $parent->add($child, 'child'); - $this->assertEquals(1, count($parent)); - $this->assertSame($child, $parent->get('child')); - $parent->remove('child'); - $this->assertEquals(0, count($parent)); + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects an instance of Zend\InputFilter\InputInterface or Zend\InputFilter\InputFilterInterface ' . + 'as its first argument; received "stdClass"' + ); + /** @noinspection PhpParamsInspection */ + $inputFilter->add(new stdClass()); } - public function getInputFilter() + public function testGetThrowExceptionIfInputDoesNotExists() { - $filter = new InputFilter(); - - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 6)); - - $bar = new Input(); - $bar->getFilterChain()->attachByName('stringtrim'); - $bar->getValidatorChain()->attach(new Validator\Digits()); - - $baz = new Input(); - $baz->setRequired(false); - $baz->getFilterChain()->attachByName('stringtrim'); - $baz->getValidatorChain()->attach(new Validator\StringLength(1, 6)); - - $qux = new Input(); - $qux->setAllowEmpty(true); - $qux->getFilterChain()->attachByName('stringtrim'); - $qux->getValidatorChain()->attach(new Validator\StringLength(5, 6)); - - $filter->add($foo, 'foo') - ->add($bar, 'bar') - ->add($baz, 'baz') - ->add($qux, 'qux') - ->add($this->getChildInputFilter(), 'nest'); - - return $filter; - } + $inputFilter = $this->inputFilter; - public function getChildInputFilter() - { - $filter = new InputFilter(); - - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 6)); - - $bar = new Input(); - $bar->getFilterChain()->attachByName('stringtrim'); - $bar->getValidatorChain()->attach(new Validator\Digits()); - - $baz = new Input(); - $baz->setRequired(false); - $baz->getFilterChain()->attachByName('stringtrim'); - $baz->getValidatorChain()->attach(new Validator\StringLength(1, 6)); - - $filter->add($foo, 'foo') - ->add($bar, 'bar') - ->add($baz, 'baz'); - return $filter; + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'no input found matching "not exists"' + ); + $inputFilter->get('not exists'); } - public function dataSets() + public function testReplaceWithInvalidInputTypeThrowsInvalidArgumentException() { - return array( - 'valid-with-empty-and-null' => array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => null, - 'qux' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => null, - ), - ), - true, - ), - 'valid-with-empty' => array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'qux' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - ), - ), - true, - ), - 'invalid-with-empty-and-missing' => array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => 'thisistoolong', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => 'thisistoolong', - ), - ), - false, - ), - 'invalid-with-empty' => array( - array( - 'foo' => ' baz bat ', - 'bar' => 'abc45', - 'baz' => ' ', - 'qux' => ' ', - 'nest' => array( - 'foo' => ' baz bat ', - 'bar' => '123ab', - 'baz' => ' ', - ), - ), - false, - ), + $inputFilter = $this->inputFilter; + $inputFilter->add(new Input('foo'), 'replace_me'); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects an instance of Zend\InputFilter\InputInterface or Zend\InputFilter\InputFilterInterface ' + . 'as its first argument; received "stdClass"' ); + /** @noinspection PhpParamsInspection */ + $inputFilter->replace(new stdClass(), 'replace_me'); } - /** - * @dataProvider dataSets - * @group fmlife - */ - public function testCanValidateEntireDataset($dataset, $expected) + public function testReplaceThrowExceptionIfInputToReplaceDoesNotExists() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $filter->setData($dataset); - $this->assertSame($expected, $filter->isValid()); + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'no input found matching "not exists"' + ); + $inputFilter->replace(new Input('foo'), 'not exists'); } - public function testCanValidatePartialDataset() + public function testGetValueThrowExceptionIfInputDoesNotExists() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $validData = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - ); - $filter->setValidationGroup('foo', 'bar'); - $filter->setData($validData); - $this->assertTrue($filter->isValid()); - - $invalidData = array( - 'bar' => 'abc45', - 'nest' => array( - 'foo' => ' 123bat ', - 'bar' => '123ab', - ), + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + '"not exists" was not found in the filter' ); - $filter->setValidationGroup('bar', 'nest'); - $filter->setData($invalidData); - $this->assertFalse($filter->isValid()); + $inputFilter->getValue('not exists'); } - public function testResetEmptyValidationGroupRecursively() + public function testGetRawValueThrowExceptionIfInputDoesNotExists() { - $data = array( - 'flat' => 'foo', - 'deep' => array( - 'deep-input1' => 'deep-foo1', - 'deep-input2' => 'deep-foo2', - ) + $inputFilter = $this->inputFilter; + + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + '"not exists" was not found in the filter' ); - $filter = new InputFilter; - $filter->add(new Input, 'flat'); - $deepInputFilter = new InputFilter; - $deepInputFilter->add(new Input, 'deep-input1'); - $deepInputFilter->add(new Input, 'deep-input2'); - $filter->add($deepInputFilter, 'deep'); - $filter->setData($data); - $filter->setValidationGroup(array('deep' => 'deep-input1')); - // reset validation group - $filter->setValidationGroup(InputFilter::VALIDATE_ALL); - $this->assertEquals($data, $filter->getValues()); + $inputFilter->getRawValue('not exists'); } - public function testSetDeepValidationGroupToNonInputFilterThrowsException() + public function testSetDataWithInvalidDataTypeThrowsInvalidArgumentException() { - $filter = $this->getInputFilter(); - $filter->add(new Input, 'flat'); - // we expect setValidationGroup to throw an exception when flat is treated - // like an inputfilter which it actually isn't + $inputFilter = $this->inputFilter; + $this->setExpectedException( 'Zend\InputFilter\Exception\InvalidArgumentException', - 'Input "flat" must implement InputFilterInterface' + 'expects an array or Traversable argument; received stdClass' ); - $filter->setValidationGroup(array('flat' => 'foo')); + /** @noinspection PhpParamsInspection */ + $inputFilter->setData(new stdClass()); } - public function testCanRetrieveInvalidInputsOnFailedValidation() + public function testIsValidThrowExceptionIfDataWasNotSetYet() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $invalidData = array( - 'foo' => ' bazbat ', - 'bar' => 'abc45', - 'nest' => array( - 'foo' => ' baz bat boo ', - 'bar' => '12345', - ), + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'no data present to validate' ); - $filter->setData($invalidData); - $this->assertFalse($filter->isValid()); - $invalidInputs = $filter->getInvalidInput(); - $this->assertArrayNotHasKey('foo', $invalidInputs); - $this->assertArrayHasKey('bar', $invalidInputs); - $this->assertInstanceOf('Zend\InputFilter\Input', $invalidInputs['bar']); - $this->assertArrayHasKey('nest', $invalidInputs/*, var_export($invalidInputs, 1)*/); - $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $invalidInputs['nest']); - $nestInvalids = $invalidInputs['nest']->getInvalidInput(); - $this->assertArrayHasKey('foo', $nestInvalids); - $this->assertInstanceOf('Zend\InputFilter\Input', $nestInvalids['foo']); - $this->assertArrayNotHasKey('bar', $nestInvalids); + $inputFilter->isValid(); } - public function testCanRetrieveValidInputsOnFailedValidation() + public function testSetValidationGroupThrowExceptionIfInputIsNotAnInputFilter() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $invalidData = array( - 'foo' => ' bazbat ', - 'bar' => 'abc45', - 'nest' => array( - 'foo' => ' baz bat ', - 'bar' => '12345', - ), + /** @var InputInterface|MockObject $nestedInput */ + $nestedInput = $this->getMock('Zend\InputFilter\InputInterface'); + $inputFilter->add($nestedInput, 'fooInput'); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'Input "fooInput" must implement InputFilterInterface' ); - $filter->setData($invalidData); - $this->assertFalse($filter->isValid()); - $validInputs = $filter->getValidInput(); - $this->assertArrayHasKey('foo', $validInputs); - $this->assertInstanceOf('Zend\InputFilter\Input', $validInputs['foo']); - $this->assertArrayNotHasKey('bar', $validInputs); - $this->assertArrayHasKey('nest', $validInputs); - $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $validInputs['nest']); - $nestValids = $validInputs['nest']->getValidInput(); - $this->assertArrayHasKey('foo', $nestValids); - $this->assertInstanceOf('Zend\InputFilter\Input', $nestValids['foo']); - $this->assertArrayHasKey('bar', $nestValids); - $this->assertInstanceOf('Zend\InputFilter\Input', $nestValids['bar']); + $inputFilter->setValidationGroup(array('fooInput' => 'foo')); } - public function testValuesRetrievedAreFiltered() + public function testSetValidationGroupThrowExceptionIfInputFilterNotExists() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $validData = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'qux' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - ), - ); - $filter->setData($validData); - $this->assertTrue($filter->isValid()); - $expected = array( - 'foo' => 'bazbat', - 'bar' => '12345', - 'baz' => null, - 'qux' => '', - 'nest' => array( - 'foo' => 'bazbat', - 'bar' => '12345', - 'baz' => null, - ), + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects a list of valid input names; "anotherNotExistsInputFilter" was not found' ); - $this->assertEquals($expected, $filter->getValues()); + $inputFilter->setValidationGroup(array('notExistInputFilter' => 'anotherNotExistsInputFilter')); } - public function testCanGetRawInputValues() + public function testSetValidationGroupThrowExceptionIfInputFilterInArgumentListNotExists() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $validData = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => null, - 'qux' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => null, - ), + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects a list of valid input names; "notExistInputFilter" was not found' ); - $filter->setData($validData); - $this->assertTrue($filter->isValid()); - $this->assertEquals($validData, $filter->getRawValues()); + $inputFilter->setValidationGroup('notExistInputFilter'); } - public function testCanGetValidationMessages() + public function testHasUnknownThrowExceptionIfDataWasNotSetYet() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $inputFilter = $this->inputFilter; - $filter = $this->getInputFilter(); - $filter->get('baz')->setRequired(true); - $filter->get('nest')->get('baz')->setRequired(true); - $invalidData = array( - 'foo' => ' bazbat boo ', - 'bar' => 'abc45', - 'baz' => '', - 'nest' => array( - 'foo' => ' baz bat boo ', - 'bar' => '123yz', - 'baz' => '', - ), + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException' ); - $filter->setData($invalidData); - $this->assertFalse($filter->isValid()); - $messages = $filter->getMessages(); - foreach ($invalidData as $key => $value) { - $this->assertArrayHasKey($key, $messages); - $currentMessages = $messages[$key]; - switch ($key) { - case 'foo': - $this->assertArrayHasKey(Validator\StringLength::TOO_LONG, $currentMessages); - break; - case 'bar': - $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $currentMessages); - break; - case 'baz': - $this->assertArrayHasKey(Validator\NotEmpty::IS_EMPTY, $currentMessages); - break; - case 'nest': - foreach ($value as $k => $v) { - $this->assertArrayHasKey($k, $messages[$key]); - $currentMessages = $messages[$key][$k]; - switch ($k) { - case 'foo': - $this->assertArrayHasKey(Validator\StringLength::TOO_LONG, $currentMessages); - break; - case 'bar': - $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $currentMessages); - break; - case 'baz': - $this->assertArrayHasKey(Validator\NotEmpty::IS_EMPTY, $currentMessages); - break; - default: - $this->fail(sprintf('Invalid key "%s" encountered in messages array', $k)); - } - } - break; - default: - $this->fail(sprintf('Invalid key "%s" encountered in messages array', $k)); - } - } + $inputFilter->hasUnknown(); } - /** - * Idea for this one is that one input may only need to be validated if another input is present. - * - * Commenting out for now, as validation context may make this irrelevant, and unsure what API to expose. - public function testCanConditionallyInvokeValidators() + public function testGetUnknownThrowExceptionIfDataWasNotSetYet() { - $this->markTestIncomplete(); + $inputFilter = $this->inputFilter; + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException' + ); + $inputFilter->getUnknown(); } - */ /** - * Idea for this one is that validation may need to rely on context -- e.g., a "password confirmation" - * field may need to know what the original password entered was in order to compare. + * Verify the state of the input filter is the desired after change it using the method `add()` + * + * @dataProvider addMethodArgumentsProvider */ - public function testValidationCanUseContext() + public function testAddHasGet($input, $name, $expectedInputName, $expectedInput) { - $filter = new InputFilter(); - - $store = new stdClass; - $foo = new Input(); - $foo->getValidatorChain()->attach(new Validator\Callback(function ($value, $context) use ($store) { - $store->value = $value; - $store->context = $context; - return true; - })); - - $bar = new Input(); - $bar->getValidatorChain()->attach(new Validator\Digits()); + $inputFilter = $this->inputFilter; + $this->assertFalse( + $inputFilter->has($expectedInputName), + "InputFilter shouldn't have an input with the name $expectedInputName yet" + ); + $currentNumberOfFilters = count($inputFilter); - $filter->add($foo, 'foo') - ->add($bar, 'bar'); + $return = $inputFilter->add($input, $name); + $this->assertSame($inputFilter, $return, "add() must return it self"); - $data = array('foo' => 'foo', 'bar' => 123); - $filter->setData($data); + // **Check input collection state** + $this->assertTrue($inputFilter->has($expectedInputName), "There is no input with name $expectedInputName"); + $this->assertCount($currentNumberOfFilters + 1, $inputFilter, 'Number of filters must be increased by 1'); - $this->assertTrue($filter->isValid()); - $this->assertEquals('foo', $store->value); - $this->assertEquals($data, $store->context); + $returnInput = $inputFilter->get($expectedInputName); + $this->assertEquals($expectedInput, $returnInput, 'get() does not match the expected input'); } /** - * Idea here is that you can indicate that if validation for a particular input fails, we should not - * attempt any further validation of any other inputs. + * Verify the state of the input filter is the desired after change it using the method `add()` and `remove()` + * + * @dataProvider addMethodArgumentsProvider */ - public function testInputBreakOnFailureFlagIsHonoredWhenValidating() + public function testAddRemove($input, $name, $expectedInputName) { - $filter = new InputFilter(); - - $store = new stdClass; - $foo = new Input(); - $foo->getValidatorChain()->attach(new Validator\Callback(function ($value, $context) use ($store) { - $store->value = $value; - $store->context = $context; - return true; - })); + $inputFilter = $this->inputFilter; - $bar = new Input(); - $bar->getValidatorChain()->attach(new Validator\Digits()); - $bar->setBreakOnFailure(true); + $inputFilter->add($input, $name); + $currentNumberOfFilters = count($inputFilter); - $filter->add($bar, 'bar') // adding bar first, as we want it to validate first and break the chain - ->add($foo, 'foo'); + $return = $inputFilter->remove($expectedInputName); + $this->assertSame($inputFilter, $return, 'remove() must return it self'); - $data = array('bar' => 'bar', 'foo' => 'foo'); - $filter->setData($data); - - $this->assertFalse($filter->isValid()); - $this->assertObjectNotHasAttribute('value', $store); - $this->assertObjectNotHasAttribute('context', $store); + $this->assertFalse($inputFilter->has($expectedInputName), "There is no input with name $expectedInputName"); + $this->assertCount($currentNumberOfFilters - 1, $inputFilter, 'Number of filters must be decreased by 1'); } - public function testValidationSkipsFieldsMarkedNotRequiredWhenNoDataPresent() + public function testAddingInputWithNameDoesNotInjectNameInInput() { - $filter = new InputFilter(); + $inputFilter = $this->inputFilter; - $foo = new Input(); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 5)); - $foo->setRequired(false); + $foo = new Input('foo'); + $inputFilter->add($foo, 'bas'); + + $test = $inputFilter->get('bas'); + $this->assertSame($foo, $test, 'get() does not match the input added'); + $this->assertEquals('foo', $foo->getName(), 'Input name should not change'); + } - $bar = new Input(); - $bar->getValidatorChain()->attach(new Validator\Digits()); - $bar->setRequired(true); + /** + * @dataProvider inputProvider + */ + public function testReplace($input, $inputName, $expectedInput) + { + $inputFilter = $this->inputFilter; + $nameToReplace = 'replace_me'; + $inputToReplace = new Input($nameToReplace); - $filter->add($foo, 'foo') - ->add($bar, 'bar'); + $inputFilter->add($inputToReplace); + $currentNumberOfFilters = count($inputFilter); - $data = array('bar' => 124); - $filter->setData($data); + $return = $inputFilter->replace($input, $nameToReplace); + $this->assertSame($inputFilter, $return, 'replace() must return it self'); + $this->assertCount($currentNumberOfFilters, $inputFilter, "Number of filters shouldn't change"); - $this->assertTrue($filter->isValid()); + $returnInput = $inputFilter->get($nameToReplace); + $this->assertEquals($expectedInput, $returnInput, 'get() does not match the expected input'); } - public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoFileDataIsPresent() - { - $filter = new InputFilter(); - - $foo = new FileInput(); - $foo->getValidatorChain()->attach(new Validator\File\UploadFile()); - $foo->setRequired(false); + /** + * @dataProvider setDataArgumentsProvider + */ + public function testSetDataAndGetRawValueGetValue( + $inputs, + $data, + $expectedRawValues, + $expectedValues, + $expectedIsValid, + $expectedInvalidInputs, + $expectedValidInputs, + $expectedMessages + ) { + $inputFilter = $this->inputFilter; + foreach ($inputs as $inputName => $input) { + $inputFilter->add($input, $inputName); + } + $return = $inputFilter->setData($data); + $this->assertSame($inputFilter, $return, 'setData() must return it self'); + + // ** Check filter state ** + $this->assertSame($expectedRawValues, $inputFilter->getRawValues(), 'getRawValues() value not match'); + foreach ($expectedRawValues as $inputName => $expectedRawValue) { + $this->assertSame( + $expectedRawValue, + $inputFilter->getRawValue($inputName), + 'getRawValue() value not match for input ' . $inputName + ); + } - $filter->add($foo, 'foo'); + $this->assertSame($expectedValues, $inputFilter->getValues(), 'getValues() value not match'); + foreach ($expectedValues as $inputName => $expectedValue) { + $this->assertSame( + $expectedValue, + $inputFilter->getValue($inputName), + 'getValue() value not match for input ' . $inputName + ); + } - $data = array( - 'foo' => array( - 'tmp_name' => '/tmp/barfile', - 'name' => 'barfile', - 'type' => 'text', - 'size' => 0, - 'error' => 4, // UPLOAD_ERR_NO_FILE - ) - ); - $filter->setData($data); - $this->assertTrue($filter->isValid()); + // ** Check validation state ** + $this->assertEquals($expectedIsValid, $inputFilter->isValid(), 'isValid() value not match'); + $this->assertEquals($expectedInvalidInputs, $inputFilter->getInvalidInput(), 'getInvalidInput() value not match'); + $this->assertEquals($expectedValidInputs, $inputFilter->getValidInput(), 'getValidInput() value not match'); + $this->assertEquals($expectedMessages, $inputFilter->getMessages(), 'getMessages() value not match'); - // Negative test - $foo->setRequired(true); - $filter->setData($data); - $this->assertFalse($filter->isValid()); + // ** Check unknown fields ** + $this->assertFalse($inputFilter->hasUnknown(), 'hasUnknown() value not match'); + $this->assertEmpty($inputFilter->getUnknown(), 'getUnknown() value not match'); } - public function testValidationSkipsFileInputsMarkedNotRequiredWhenNoMultiFileDataIsPresent() - { - $filter = new InputFilter(); - $foo = new FileInput(); - $foo->setRequired(false); - $filter->add($foo, 'foo'); - - $data = array( - 'foo' => array(array( - 'tmp_name' => '/tmp/barfile', - 'name' => 'barfile', - 'type' => 'text', - 'size' => 0, - 'error' => 4, // UPLOAD_ERR_NO_FILE - )), + /** + * @dataProvider setDataArgumentsProvider + */ + public function testSetTraversableDataAndGetRawValueGetValue( + $inputs, + $data, + $expectedRawValues, + $expectedValues, + $expectedIsValid, + $expectedInvalidInputs, + $expectedValidInputs, + $expectedMessages + ) { + $dataTypes = $this->dataTypes(); + $this->testSetDataAndGetRawValueGetValue( + $inputs, + $dataTypes['Traversable']($data), + $expectedRawValues, + $expectedValues, + $expectedIsValid, + $expectedInvalidInputs, + $expectedValidInputs, + $expectedMessages ); - $filter->setData($data); - $this->assertTrue($filter->isValid()); - - // Negative test - $foo->setRequired(true); - $filter->setData($data); - $this->assertFalse($filter->isValid()); } - public function testValidationAllowsEmptyValuesToRequiredInputWhenAllowEmptyFlagIsTrue() + public function testResetEmptyValidationGroupRecursively() { - $filter = new InputFilter(); - - $foo = new Input('foo'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 5)); - $foo->setRequired(true); - $foo->setAllowEmpty(true); - - $bar = new Input(); - $bar->getValidatorChain()->attach(new Validator\Digits()); - $bar->setRequired(true); - - $filter->add($foo, '') - ->add($bar, 'bar'); - $data = array( - 'bar' => 124, - 'foo' => '', + 'flat' => 'foo', + 'deep' => array( + 'deep-input1' => 'deep-foo1', + 'deep-input2' => 'deep-foo2', + ) ); - + $expectedData = array_merge($data, array('notSet' => null)); + /** @var Input|MockObject $resetInput */ + $flatInput = $this->getMockBuilder('Zend\InputFilter\Input') + ->enableProxyingToOriginalMethods() + ->setConstructorArgs(array('flat')) + ->getMock() + ; + $flatInput->expects($this->once()) + ->method('setValue') + ->with('foo') + ; + // Inputs without value must be reset for to have clean states when use different setData arguments + /** @var Input|MockObject $flatInput */ + $resetInput = $this->getMockBuilder('Zend\InputFilter\Input') + ->enableProxyingToOriginalMethods() + ->setConstructorArgs(array('notSet')) + ->getMock() + ; + $resetInput->expects($this->once()) + ->method('resetValue') + ; + + $filter = $this->inputFilter; + $filter->add($flatInput); + $filter->add($resetInput); + $deepInputFilter = new BaseInputFilter; + $deepInputFilter->add(new Input, 'deep-input1'); + $deepInputFilter->add(new Input, 'deep-input2'); + $filter->add($deepInputFilter, 'deep'); $filter->setData($data); - - $this->assertTrue($filter->isValid()); - $this->assertEquals('', $filter->getValue('foo')); + $filter->setValidationGroup(array('deep' => 'deep-input1')); + // reset validation group + $filter->setValidationGroup(InputFilterInterface::VALIDATE_ALL); + $this->assertEquals($expectedData, $filter->getValues()); } - public function testValidationMarksInputInvalidWhenRequiredAndAllowEmptyFlagIsFalse() - { - $filter = new InputFilter(); - - $foo = new Input(); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 5)); - $foo->setRequired(true); - $foo->setAllowEmpty(false); - - $bar = new Input(); - $bar->getValidatorChain()->attach(new Validator\Digits()); - $bar->setRequired(true); - - $filter->add($foo, '') - ->add($bar, 'bar'); - - $data = array('bar' => 124); - $filter->setData($data); - - $this->assertFalse($filter->isValid()); - } + /* + * Idea for this one is that validation may need to rely on context -- e.g., a "password confirmation" + * field may need to know what the original password entered was in order to compare. + */ - public static function contextDataProvider() + public function contextProvider() { + $data = array('fooInput' => 'fooValue'); + $traversableData = new ArrayObject(array('fooInput' => 'fooValue')); + $expectedFromData = array('fooInput' => 'fooValue'); + return array( - array('', 'y', true), - array('', 'n', false), + // Description => [$data, $customContext, $expectedContext] + 'by default get context from data (array)' => array($data, null, $expectedFromData), + 'by default get context from data (Traversable)' => array($traversableData, null, $expectedFromData), + 'use custom context' => array(array(), 'fooContext', 'fooContext'), ); } /** - * Idea here is that an empty field may or may not be valid based on - * context. + * @dataProvider contextProvider */ - /** - * @dataProvider contextDataProvider() - */ - // @codingStandardsIgnoreStart - public function testValidationMarksInputValidWhenAllowEmptyFlagIsTrueAndContinueIfEmptyIsTrueAndContextValidatesEmptyField($allowEmpty, $blankIsValid, $valid) + public function testValidationContext($data, $customContext, $expectedContext) { - // @codingStandardsIgnoreEnd - $filter = new InputFilter(); + $filter = $this->inputFilter; - $data = array( - 'allowEmpty' => $allowEmpty, - 'blankIsValid' => $blankIsValid, - ); + $input = $this->createInputInterfaceMock('fooInput', true, true, $expectedContext); + $filter->add($input, 'fooInput'); - $allowEmpty = new Input(); - $allowEmpty->setAllowEmpty(true) - ->setContinueIfEmpty(true); - - $blankIsValid = new Input(); - $blankIsValid->getValidatorChain()->attach(new Validator\Callback(function ($value, $context) { - return ('y' === $value && empty($context['allowEmpty'])); - })); - - $filter->add($allowEmpty, 'allowEmpty') - ->add($blankIsValid, 'blankIsValid'); $filter->setData($data); - $this->assertSame($valid, $filter->isValid()); + $this->assertTrue( + $filter->isValid($customContext), + 'isValid() value not match. Detail: ' . json_encode($filter->getMessages()) + ); } - public function testCanRetrieveRawValuesIndividuallyWithoutValidating() + public function testBuildValidationContextUsingInputGetRawValue() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } + $data = array(); + $expectedContext = array('fooInput' => 'fooRawValue'); + $filter = $this->inputFilter; + + $input = $this->createInputInterfaceMock('fooInput', true, true, $expectedContext, 'fooRawValue'); + $filter->add($input, 'fooInput'); - $filter = $this->getInputFilter(); - $data = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - ), - ); $filter->setData($data); - $test = $filter->getRawValue('foo'); - $this->assertSame($data['foo'], $test); + + $this->assertTrue( + $filter->isValid(), + 'isValid() value not match. Detail: ' . json_encode($filter->getMessages()) + ); } - public function testCanRetrieveUnvalidatedButFilteredInputValue() + public function testContextIsTheSameWhenARequiredInputIsGivenAndOptionalInputIsMissing() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $filter = $this->getInputFilter(); $data = array( - 'foo' => ' baz 2 bat ', - 'bar' => '12345', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - ), + 'inputRequired' => 'inputRequiredValue', + ); + $expectedContext = array( + 'inputRequired' => 'inputRequiredValue', + 'inputOptional' => null, ); + $inputRequired = $this->createInputInterfaceMock('fooInput', true, true, $expectedContext); + $inputOptional = $this->createInputInterfaceMock('fooInput', false); + + $filter = $this->inputFilter; + $filter->add($inputRequired, 'inputRequired'); + $filter->add($inputOptional, 'inputOptional'); + $filter->setData($data); - $test = $filter->getValue('foo'); - $this->assertSame('bazbat', $test); + + $this->assertTrue( + $filter->isValid(), + 'isValid() value not match. Detail: ' . json_encode($filter->getMessages()) + ); } - public function testGetRequiredNotEmptyValidationMessages() + public function testValidationSkipsFieldsMarkedNotRequiredWhenNoDataPresent() { - $filter = new InputFilter(); + $filter = $this->inputFilter; - $foo = new Input(); - $foo->setRequired(true); - $foo->setAllowEmpty(false); + $optionalInputName = 'fooOptionalInput'; + /** @var InputInterface|MockObject $optionalInput */ + $optionalInput = $this->getMock('Zend\InputFilter\InputInterface'); + $optionalInput->method('getName') + ->willReturn($optionalInputName) + ; + $optionalInput->expects($this->never()) + ->method('isValid') + ; + $data = array(); - $filter->add($foo, 'foo'); + $filter->add($optionalInput); - $data = array('foo' => null); $filter->setData($data); - $this->assertFalse($filter->isValid()); - $messages = $filter->getMessages(); - $this->assertArrayHasKey('foo', $messages); - $this->assertNotEmpty($messages['foo']); - } - - public function testHasUnknown() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $filter = $this->getInputFilter(); - $validData = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '' + $this->assertTrue( + $filter->isValid(), + 'isValid() value not match. Detail . ' . json_encode($filter->getMessages()) ); - $filter->setData($validData); - $this->assertFalse($filter->hasUnknown()); - - $filter = $this->getInputFilter(); - $invalidData = array( - 'bar' => '12345', - 'baz' => '', - 'gru' => '', + $this->assertArrayNotHasKey( + $optionalInputName, + $filter->getValidInput(), + 'Missing optional fields must not appear as valid input neither invalid input' ); - $filter->setData($invalidData); - $this->assertTrue($filter->hasUnknown()); - } - public function testGetUknown() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $filter = $this->getInputFilter(); - $unknown = array( - 'bar' => '12345', - 'baz' => '', - 'gru' => 10, - 'test' => 'ok', + $this->assertArrayNotHasKey( + $optionalInputName, + $filter->getInvalidInput(), + 'Missing optional fields must not appear as valid input neither invalid input' ); - $filter->setData($unknown); - $unknown = $filter->getUnknown(); - $this->assertEquals(2, count($unknown)); - $this->assertArrayHasKey('gru', $unknown); - $this->assertEquals(10, $unknown['gru']); - $this->assertArrayHasKey('test', $unknown); - $this->assertEquals('ok', $unknown['test']); - - $filter = $this->getInputFilter(); - $validData = array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '' - ); - $filter->setData($validData); - $unknown = $filter->getUnknown(); - $this->assertEquals(0, count($unknown)); } - public function testValidateUseExplodeAndInstanceOf() + /** + * @dataProvider unknownScenariosProvider + */ + public function testUnknown($inputs, $data, $hasUnknown, $getUnknown) { - $filter = new InputFilter(); - - $input = new Input(); - $input->setRequired(true); - - $input->getValidatorChain()->attach( - new \Zend\Validator\Explode( - array( - 'validator' => new \Zend\Validator\IsInstanceOf( - array( - 'className' => 'Zend\InputFilter\Input' - ) - ) - ) - ) - ); + $inputFilter = $this->inputFilter; + foreach ($inputs as $name => $input) { + $inputFilter->add($input, $name); + } - $filter->add($input, 'example'); + $inputFilter->setData($data); - $data = array( - 'example' => array( - $input - ) - ); - - $filter->setData($data); - $this->assertTrue($filter->isValid()); + $this->assertEquals($getUnknown, $inputFilter->getUnknown(), 'getUnknown() value not match'); + $this->assertEquals($hasUnknown, $inputFilter->hasUnknown(), 'hasUnknown() value not match'); } public function testGetInputs() { - $filter = new InputFilter(); + $filter = $this->inputFilter; $foo = new Input('foo'); $bar = new Input('bar'); @@ -837,7 +541,7 @@ public function testGetInputs() */ public function testAddingExistingInputWillMergeIntoExisting() { - $filter = new InputFilter(); + $filter = $this->inputFilter; $foo1 = new Input('foo'); $foo1->setRequired(true); @@ -850,52 +554,19 @@ public function testAddingExistingInputWillMergeIntoExisting() $this->assertFalse($filter->get('foo')->isRequired()); } - /** - * @group 5270 - * @requires extension intl - */ - public function testIsValidWhenValuesSetOnFilters() - { - $filter = new InputFilter(); - - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(15, 18)); - - $filter->add($foo, 'foo'); - - //test valid with setData - $filter->setData(array('foo' => 'invalid')); - $this->assertFalse($filter->isValid()); - - //test invalid with setData - $filter->setData(array('foo' => 'thisisavalidstring')); - $this->assertTrue($filter->isValid()); - - //test invalid when setting data on actual filter - $filter->get('foo')->setValue('invalid'); - $this->assertFalse($filter->get('foo')->isValid(), 'Filtered value is valid, should be invalid'); - $this->assertFalse($filter->isValid(), 'Input filter did not return value from filter'); - - //test valid when setting data on actual filter - $filter->get('foo')->setValue('thisisavalidstring'); - $this->assertTrue($filter->get('foo')->isValid(), 'Filtered value is not valid'); - $this->assertTrue($filter->isValid(), 'Input filter did return value from filter'); - } - /** * @group 5638 */ public function testPopulateSupportsArrayInputEvenIfDataMissing() { + /** @var ArrayInput|MockObject $arrayInput */ $arrayInput = $this->getMock('Zend\InputFilter\ArrayInput'); $arrayInput ->expects($this->once()) ->method('setValue') ->with(array()); - $filter = new InputFilter(); + $filter = $this->inputFilter; $filter->add($arrayInput, 'arrayInput'); $filter->setData(array('foo' => 'bar')); } @@ -905,8 +576,8 @@ public function testPopulateSupportsArrayInputEvenIfDataMissing() */ public function testMerge() { - $inputFilter = new InputFilter(); - $originInputFilter = new InputFilter(); + $inputFilter = $this->inputFilter; + $originInputFilter = new BaseInputFilter(); $inputFilter->add(new Input(), 'foo'); $inputFilter->add(new Input(), 'bar'); @@ -919,199 +590,308 @@ public function testMerge() array( 'foo', 'bar', - 'baz' + 'baz', ), array_keys($inputFilter->getInputs()) ); } - public function testAllowEmptyTestsFilteredValueAndOverrulesValidatorChain() + public function addMethodArgumentsProvider() { - $input = new Input('foo'); - $input->setAllowEmpty(true); - $input->setContinueIfEmpty(false); - // Filter chain produces empty value which should be evaluated instead of raw value - $input->getFilterChain()->attach(new Filter\Callback(function () { - return ''; - })); - // Validator chain says "not valid", but should not be invoked at all - $input->getValidatorChain()->attach(new Validator\Callback(function () { - return false; - })); - - $filter = new \Zend\InputFilter\InputFilter; - $filter->add($input) - ->setData(array('foo' => 'nonempty')); - - $this->assertTrue($filter->isValid()); - $this->assertEquals(array('foo' => ''), $filter->getValues()); - } + $inputTypes = $this->inputProvider(); - public function testAllowEmptyTestsFilteredValueAndContinuesIfEmpty() - { - $input = new Input('foo'); - $input->setAllowEmpty(true); - $input->setContinueIfEmpty(true); - // Filter chain produces empty value which should be evaluated instead of raw value - $input->getFilterChain()->attach(new Filter\Callback(function () { - return ''; - })); - // Validator chain says "not valid", explicitly requested despite empty input - $input->getValidatorChain()->attach(new Validator\Callback(function () { - return false; - })); - - $filter = new \Zend\InputFilter\InputFilter; - $filter->add($input) - ->setData(array('foo' => 'nonempty')); - - $this->assertFalse($filter->isValid()); - } - - /** - * @group 7 - */ - public function testMissingRequiredAllowedEmptyValueShouldMarkInputFilterInvalid() - { - $foo = new Input('foo'); - $foo->setRequired(true); - $foo->setAllowEmpty(false); + $inputName = function ($inputTypeData) { + return $inputTypeData[1]; + }; - $bar = new Input('bar'); - $bar->setRequired(true); - $bar->setAllowEmpty(true); + $sameInput = function ($inputTypeData) { + return $inputTypeData[2]; + }; - $filter = new InputFilter(); - $filter->add($foo); - $filter->add($bar); + // @codingStandardsIgnoreStart + $dataTemplates = array( + // Description => [[$input argument], $name argument, $expectedName, $expectedInput] + 'null' => array($inputTypes, null , $inputName , $sameInput), + 'custom_name' => array($inputTypes, 'custom_name', 'custom_name', $sameInput), + ); + // @codingStandardsIgnoreEnd - $filter->setData(array('foo' => 'xyz')); - $this->assertFalse($filter->isValid(), 'Missing required value should mark input filter as invalid'); - } + // Expand data template matrix for each possible input type. + // Description => [$input argument, $name argument, $expectedName, $expectedInput] + $dataSets = array(); + foreach ($dataTemplates as $dataTemplateDescription => $dataTemplate) { + foreach ($dataTemplate[0] as $inputTypeDescription => $inputTypeData) { + $tmpTemplate = $dataTemplate; + $tmpTemplate[0] = $inputTypeData[0]; // expand input + if (is_callable($dataTemplate[2])) { + $tmpTemplate[2] = $dataTemplate[2]($inputTypeData); + } + $tmpTemplate[3] = $dataTemplate[3]($inputTypeData); + + $dataSets[$inputTypeDescription . ' / ' . $dataTemplateDescription] = $tmpTemplate; + } + } - public function emptyValuesForValidation() - { - return array( - 'null' => array(null), - 'empty-string' => array(''), + return $dataSets; + } + + public function setDataArgumentsProvider() + { + $iAName = 'InputA'; + $iBName = 'InputB'; + $vRaw = 'rawValue'; + $vFiltered = 'filteredValue'; + + $dARaw = array($iAName => $vRaw); + $dBRaw = array($iBName => $vRaw); + $dAfRaw = array($iAName => array('fooInput' => $vRaw)); + $d2Raw = array_merge($dARaw, $dBRaw); + $dAfBRaw = array_merge($dAfRaw, $dBRaw); + $dAFiltered = array($iAName => $vFiltered); + $dBFiltered = array($iBName => $vFiltered); + $dAfFiltered = array($iAName => array('fooInput' => $vFiltered)); + $d2Filtered = array_merge($dAFiltered, $dBFiltered); + $dAfBFiltered = array_merge($dAfFiltered, $dBFiltered); + + $required = true; + $valid = true; + $bOnFail = true; + + $self = $this; + $input = function ($iName, $required, $bOnFail, $isValid, $msg = array()) use ($vRaw, $vFiltered, $self) { + // @codingStandardsIgnoreStart + return function ($context) use ($iName, $required, $bOnFail, $isValid, $vRaw, $vFiltered, $msg, $self) { + return $self->createInputInterfaceMock($iName, $required, $isValid, $context, $vRaw, $vFiltered, $msg, $bOnFail); + }; + // @codingStandardsIgnoreEnd + }; + + $inputFilter = function ($isValid, $msg = array()) use ($vRaw, $vFiltered, $self) { + // @codingStandardsIgnoreStart + return function ($context) use ($isValid, $vRaw, $vFiltered, $msg, $self) { + $vRaw = array('fooInput' => $vRaw); + $vFiltered = array('fooInput' => $vFiltered); + return $self->createInputFilterInterfaceMock($isValid, $context, $vRaw, $vFiltered, $msg); + }; + // @codingStandardsIgnoreEnd + }; + + // @codingStandardsIgnoreStart + $iAri = array($iAName => $input($iAName, $required, !$bOnFail, !$valid, array('Invalid ' . $iAName))); + $iAriX = array($iAName => $input($iAName, $required, $bOnFail, !$valid, array('Invalid ' . $iAName))); + $iArvX = array($iAName => $input($iAName, $required, $bOnFail, $valid, array())); + $iBri = array($iBName => $input($iBName, $required, !$bOnFail, !$valid, array('Invalid ' . $iBName))); + $iBriX = array($iBName => $input($iBName, $required, $bOnFail, !$valid, array('Invalid ' . $iBName))); + $iBrvX = array($iBName => $input($iBName, $required, $bOnFail, $valid, array())); + $ifAi = array($iAName => $inputFilter(!$valid, array('fooInput' => array('Invalid ' . $iAName)))); + $ifAv = array($iAName => $inputFilter($valid)); + $iAriBri = array_merge($iAri, $iBri); + $iArvXBrvX = array_merge($iArvX, $iBrvX); + $iAriBrvX = array_merge($iAri, $iBrvX); + $iArvXBir = array_merge($iArvX, $iBri); + $iAriXBrvX = array_merge($iAriX, $iBrvX); + $iArvXBriX = array_merge($iArvX, $iBriX); + $iAriXBriX = array_merge($iAriX, $iBriX); + $ifAiBri = array_merge($ifAi, $iBri); + $ifAiBrvX = array_merge($ifAi, $iBrvX); + $ifAvBri = array_merge($ifAv, $iBri); + $ifAvBrv = array_merge($ifAv, $iBrvX); + + $msgAInv = array($iAName => array('Invalid InputA')); + $msgBInv = array($iBName => array('Invalid InputB')); + $msgAfInv = array($iAName => array('fooInput' => array('Invalid InputA'))); + $msg2Inv = array_merge($msgAInv, $msgBInv); + $msgAfBInv = array_merge($msgAfInv, $msgBInv); + + $dataSets = array( + // Description => [$inputs, $data argument, $expectedRawValues, $expectedValues, $expectedIsValid, + // $expectedInvalidInputs, $expectedValidInputs, $expectedMessages] + 'invalid Break invalid' => array($iAriXBriX, $d2Raw , $d2Raw , $d2Filtered , false, $iAri , array() , $msgAInv), + 'invalid Break valid' => array($iAriXBrvX, $d2Raw , $d2Raw , $d2Filtered , false, $iAri , array() , $msgAInv), + 'valid Break invalid' => array($iArvXBriX, $d2Raw , $d2Raw , $d2Filtered , false, $iBri , $iAri , $msgBInv), + 'valid Break valid' => array($iArvXBrvX, $d2Raw , $d2Raw , $d2Filtered , true , array() , $iArvXBrvX, array()), + 'valid invalid' => array($iArvXBir , $d2Raw , $d2Raw , $d2Filtered , false, $iBri , $iArvX , $msgBInv), + 'IInvalid IValid' => array($iAriBrvX , $d2Raw , $d2Raw , $d2Filtered , false, $iAri , $iBrvX , $msgAInv), + 'IInvalid IInvalid' => array($iAriBri , $d2Raw , $d2Raw , $d2Filtered , false, $iAriBri , array() , $msg2Inv), + 'IInvalid IValid / Partial' => array($iAriBri , $dARaw , $d2Raw , $d2Filtered , false, $iAriBrvX, array() , $msg2Inv), + 'IFInvalid IValid' => array($ifAiBrvX , $dAfBRaw, $dAfBRaw, $dAfBFiltered, false, $ifAi , $iBrvX , $msgAfInv), + 'IFInvalid IInvalid' => array($ifAiBri , $dAfBRaw, $dAfBRaw, $dAfBFiltered, false, $ifAiBri , array() , $msgAfBInv), + 'IFValid IInvalid' => array($ifAvBri , $dAfBRaw, $dAfBRaw, $dAfBFiltered, false, $iBri , $ifAv , $msgBInv), + 'IFValid IValid' => array($ifAvBrv , $dAfBRaw, $dAfBRaw, $dAfBFiltered, true , array() , $ifAvBrv , array()), ); - } - - /** - * @group 7 - * @dataProvider emptyValuesForValidation - */ - public function testEmptyValuePassedForRequiredButAllowedEmptyInputShouldMarkInputFilterValid($value) - { - $foo = new Input('foo'); - $foo->setRequired(true); - $foo->setAllowEmpty(false); + // @codingStandardsIgnoreEnd - $bar = new Input('bar'); - $bar->setRequired(true); - $bar->setAllowEmpty(true); + array_walk( + $dataSets, + function (&$set) { + // Create unique mock input instances for each set + foreach ($set[0] as $name => $createMock) { + $input = $createMock($set[2]); - $filter = new InputFilter(); - $filter->add($foo); - $filter->add($bar); + $set[0][$name] = $input; + if (in_array($name, array_keys($set[5]))) { + $set[5][$name] = $input; + } + if (in_array($name, array_keys($set[6]))) { + $set[6][$name] = $input; + } + } + } + ); - $filter->setData(array( - 'foo' => 'xyz', - 'bar' => $value, - )); - $this->assertTrue($filter->isValid(), 'Empty value should mark input filter as valid'); + return $dataSets; } - /** - * @group 10 - */ - public function testMissingRequiredWithFallbackShouldMarkInputValid() + public function unknownScenariosProvider() { - $foo = new Input('foo'); - $foo->setRequired(true); - $foo->setAllowEmpty(false); + $inputA = $this->createInputInterfaceMock('inputA', true); + $dataA = array('inputA' => 'foo'); + $dataUnknown = array('inputUnknown' => 'unknownValue'); + $dataAAndUnknown = array_merge($dataA, $dataUnknown); - $bar = new Input('bar'); - $bar->setRequired(true); - $bar->setFallbackValue('baz'); + // @codingStandardsIgnoreStart + return array( + // Description => [$inputs, $data, $hasUnknown, $getUnknown] + 'empty data and inputs' => array(array() , array() , false, array()), + 'empty data' => array(array($inputA), array() , false, array()), + 'data and fields match' => array(array($inputA), $dataA , false, array()), + 'data known and unknown' => array(array($inputA), $dataAAndUnknown, true , $dataUnknown), + 'data unknown' => array(array($inputA), $dataUnknown , true , $dataUnknown), + 'data unknown, no input' => array(array() , $dataUnknown , true , $dataUnknown), + ); + // @codingStandardsIgnoreEnd + } - $filter = new InputFilter(); - $filter->add($foo); - $filter->add($bar); + public function inputProvider() + { + $input = $this->createInputInterfaceMock('fooInput', null); + $inputFilter = $this->createInputFilterInterfaceMock(); - $filter->setData(array('foo' => 'xyz')); - $this->assertTrue($filter->isValid(), 'Missing input with fallback value should mark input filter as valid'); - $data = $filter->getValues(); - $this->assertArrayHasKey('bar', $data); - $this->assertEquals($bar->getFallbackValue(), $data['bar']); + // @codingStandardsIgnoreStart + return array( + // Description => [input, expected name, $expectedReturnInput] + 'InputInterface' => array($input , 'fooInput', $input), + 'InputFilterInterface' => array($inputFilter, null , $inputFilter), + ); + // @codingStandardsIgnoreEnd } /** - * @group 10 + * @param null|bool $isValid + * @param mixed $context + * @param mixed[] $getRawValues + * @param mixed[] $getValues + * @param string[] $getMessages + * + * @return MockObject|InputFilterInterface */ - public function testMissingRequiredThatAllowsEmptyWithFallbackShouldMarkInputValid() - { - $foo = new Input('foo'); - $foo->setRequired(true); - $foo->setAllowEmpty(false); - - $bar = new Input('bar'); - $bar->setRequired(true); - $bar->setAllowEmpty(true); - $bar->setFallbackValue('baz'); - - $filter = new InputFilter(); - $filter->add($foo); - $filter->add($bar); + public function createInputFilterInterfaceMock( + $isValid = null, + $context = null, + $getRawValues = array(), + $getValues = array(), + $getMessages = array() + ) { + /** @var InputFilterInterface|MockObject $inputFilter */ + $inputFilter = $this->getMock('Zend\InputFilter\InputFilterInterface'); + $inputFilter->method('getRawValues') + ->willReturn($getRawValues) + ; + $inputFilter->method('getValues') + ->willReturn($getValues) + ; + if (($isValid === false) || ($isValid === true)) { + $inputFilter->expects($this->once()) + ->method('isValid') + ->willReturn($isValid) + ; + } else { + $inputFilter->expects($this->never()) + ->method('isValid') + ; + } + $inputFilter->method('getMessages') + ->willReturn($getMessages) + ; - $filter->setData(array('foo' => 'xyz')); - $this->assertTrue($filter->isValid(), 'Missing input with fallback value should mark input filter as valid'); - $data = $filter->getValues(); - $this->assertArrayHasKey('bar', $data); - $this->assertEquals($bar->getFallbackValue(), $data['bar']); + return $inputFilter; } /** - * @group 10 + * @param string $name + * @param bool $isRequired + * @param null|bool $isValid + * @param mixed $context + * @param mixed $getRawValue + * @param mixed $getValue + * @param string[] $getMessages + * @param bool $breakOnFailure + * + * @return MockObject|InputInterface */ - public function testEmptyRequiredValueWithFallbackShouldMarkInputValid() - { - $foo = new Input('foo'); - $foo->setRequired(true); - $foo->setAllowEmpty(false); - - $bar = new Input('bar'); - $bar->setRequired(true); - $bar->setFallbackValue('baz'); - - $filter = new InputFilter(); - $filter->add($foo); - $filter->add($bar); + public function createInputInterfaceMock( + $name, + $isRequired, + $isValid = null, + $context = null, + $getRawValue = null, + $getValue = null, + $getMessages = array(), + $breakOnFailure = false + ) { + /** @var InputInterface|MockObject $input */ + $input = $this->getMock('Zend\InputFilter\InputInterface'); + $input->method('getName') + ->willReturn($name) + ; + $input->method('isRequired') + ->willReturn($isRequired) + ; + $input->method('getRawValue') + ->willReturn($getRawValue) + ; + $input->method('getValue') + ->willReturn($getValue) + ; + $input->method('breakOnFailure') + ->willReturn($breakOnFailure) + ; + if (($isValid === false) || ($isValid === true)) { + $input->expects($this->once()) + ->method('isValid') + ->with($context) + ->willReturn($isValid) + ; + } else { + $input->expects($this->never()) + ->method('isValid') + ->with($context) + ; + } + $input->method('getMessages') + ->willReturn($getMessages) + ; - $filter->setData(array( - 'foo' => 'xyz', - 'bar' => null, - )); - $this->assertTrue($filter->isValid(), 'Empty input with fallback value should mark input filter as valid'); - $data = $filter->getValues(); - $this->assertArrayHasKey('bar', $data); - $this->assertEquals($bar->getFallbackValue(), $data['bar']); + return $input; } /** - * @group 15 + * @return callable[] */ - public function testAllowsValidatingArrayAccessData() + protected function dataTypes() { - $filter = new InputFilter(); - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 6)); - $filter->add($foo, 'foo'); - - $data = new ArrayObject(array('foo' => ' valid ')); - $filter->setData($data); - $this->assertTrue($filter->isValid()); + $self = $this; + return array( + // Description => callable + 'array' => function ($data) { + return $data; + }, + 'Traversable' => function ($data) use ($self) { + return $self->getMockBuilder('FilterIterator') + ->setConstructorArgs(array(new ArrayIterator($data))) + ->getMock() + ; + }, + ); } } diff --git a/test/CollectionInputFilterTest.php b/test/CollectionInputFilterTest.php index 39156959..ee91a9f3 100644 --- a/test/CollectionInputFilterTest.php +++ b/test/CollectionInputFilterTest.php @@ -9,145 +9,80 @@ namespace ZendTest\InputFilter; +use ArrayIterator; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_TestCase as TestCase; +use stdClass; use Zend\InputFilter\BaseInputFilter; use Zend\InputFilter\CollectionInputFilter; use Zend\InputFilter\Input; use Zend\InputFilter\InputFilter; -use Zend\Validator; +/** + * @covers Zend\InputFilter\CollectionInputFilter + */ class CollectionInputFilterTest extends TestCase { /** - * @var \Zend\InputFilter\CollectionInputFilter + * @var CollectionInputFilter */ - protected $filter; + protected $inputFilter; public function setUp() { - $this->filter = new CollectionInputFilter(); - } - - public function getBaseInputFilter() - { - $filter = new BaseInputFilter(); - - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 6)); - - $bar = new Input(); - $bar->getFilterChain()->attachByName('stringtrim'); - $bar->getValidatorChain()->attach(new Validator\Digits()); - - $baz = new Input(); - $baz->setRequired(false); - $baz->getFilterChain()->attachByName('stringtrim'); - $baz->getValidatorChain()->attach(new Validator\StringLength(1, 6)); - - $filter->add($foo, 'foo') - ->add($bar, 'bar') - ->add($baz, 'baz') - ->add($this->getChildInputFilter(), 'nest'); - - return $filter; + $this->inputFilter = new CollectionInputFilter(); } - public function getChildInputFilter() + public function testSetInputFilterWithInvalidTypeThrowsInvalidArgumentException() { - $filter = new BaseInputFilter(); - - $foo = new Input(); - $foo->getFilterChain()->attachByName('stringtrim') - ->attachByName('alpha'); - $foo->getValidatorChain()->attach(new Validator\StringLength(3, 6)); - - $bar = new Input(); - $bar->getFilterChain()->attachByName('stringtrim'); - $bar->getValidatorChain()->attach(new Validator\Digits()); - - $baz = new Input(); - $baz->setRequired(false); - $baz->getFilterChain()->attachByName('stringtrim'); - $baz->getValidatorChain()->attach(new Validator\StringLength(1, 6)); - - $filter->add($foo, 'foo') - ->add($bar, 'bar') - ->add($baz, 'baz'); - return $filter; - } + $inputFilter = $this->inputFilter; - public function getValidCollectionData() - { - return array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ), - array( - 'foo' => ' batbaz ', - 'bar' => '54321', - 'baz' => '', - 'nest' => array( - 'foo' => ' batbaz ', - 'bar' => '54321', - 'baz' => '', - ), - ) + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'expects an instance of Zend\InputFilter\BaseInputFilter; received "stdClass"' ); + /** @noinspection PhpParamsInspection */ + $inputFilter->setInputFilter(new stdClass()); } - public function testSetInputFilter() - { - $this->filter->setInputFilter(new BaseInputFilter()); - $this->assertInstanceOf('Zend\InputFilter\BaseInputFilter', $this->filter->getInputFilter()); - } - public function testGetDefaultInputFilter() + /** + * @dataProvider inputFilterProvider + */ + public function testSetInputFilter($inputFilter, $expectedType) { - $this->assertInstanceOf('Zend\InputFilter\BaseInputFilter', $this->filter->getInputFilter()); - } + $this->inputFilter->setInputFilter($inputFilter); - public function testSetCount() - { - $this->filter->setCount(5); - $this->assertEquals(5, $this->filter->getCount()); + $this->assertInstanceOf($expectedType, $this->inputFilter->getInputFilter(), 'getInputFilter() type not match'); } - public function testSetCountBelowZero() + public function testGetDefaultInputFilter() { - $this->filter->setCount(-1); - $this->assertEquals(0, $this->filter->getCount()); + $this->assertInstanceOf('Zend\InputFilter\BaseInputFilter', $this->inputFilter->getInputFilter()); } - public function testGetCountUsesCountOfCollectionDataWhenNotSet() + /** + * @dataProvider isRequiredProvider + */ + public function testSetRequired($value) { - $collectionData = array( - array('foo' => 'bar'), - array('foo' => 'baz') - ); - - $this->filter->setData($collectionData); - $this->assertEquals(2, $this->filter->getCount()); + $this->inputFilter->setIsRequired($value); + $this->assertEquals($value, $this->inputFilter->getIsRequired()); } - public function testGetCountUsesSpecifiedCount() + /** + * @dataProvider countVsDataProvider + */ + public function testSetCount($count, $data, $expectedCount) { - $collectionData = array( - array('foo' => 'bar'), - array('foo' => 'baz') - ); + if ($count !== null) { + $this->inputFilter->setCount($count); + } + if ($data !== null) { + $this->inputFilter->setData($data); + } - $this->filter->setCount(3); - $this->filter->setData($collectionData); - $this->assertEquals(3, $this->filter->getCount()); + $this->assertEquals($expectedCount, $this->inputFilter->getCount(), 'getCount() value not match'); } /** @@ -157,546 +92,140 @@ public function testGetCountReturnsRightCountOnConsecutiveCallsWithDifferentData { $collectionData1 = array( array('foo' => 'bar'), - array('foo' => 'baz') + array('foo' => 'baz'), ); $collectionData2 = array( - array('foo' => 'bar') - ); - - $this->filter->setData($collectionData1); - $this->assertEquals(2, $this->filter->getCount()); - $this->filter->setData($collectionData2); - $this->assertEquals(1, $this->filter->getCount()); - } - - public function testCanValidateValidData() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($this->getValidCollectionData()); - $this->assertTrue($this->filter->isValid()); - } - - public function testCanValidateValidDataWithNonConsecutiveKeys() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $collectionData = $this->getValidCollectionData(); - $collectionData[2] = $collectionData[0]; - unset($collectionData[0]); - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($collectionData); - $this->assertTrue($this->filter->isValid()); - } - - public function testInvalidDataReturnsFalse() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $invalidCollectionData = array( - array( - 'foo' => ' bazbatlong ', - 'bar' => '12345', - 'baz' => '', - ), - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ) + array('foo' => 'bar'), ); - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($invalidCollectionData); - $this->assertFalse($this->filter->isValid()); + $this->inputFilter->setData($collectionData1); + $this->assertEquals(2, $this->inputFilter->getCount()); + $this->inputFilter->setData($collectionData2); + $this->assertEquals(1, $this->inputFilter->getCount()); } - public function testDataLessThanCountIsInvalid() + public function testInvalidCollectionIsNotValid() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $invalidCollectionData = array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ), - ); + $data = 1; + $this->inputFilter->setData($data); - $this->filter->setCount(2); - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($invalidCollectionData); - $this->assertFalse($this->filter->isValid()); + $this->assertFalse($this->inputFilter->isValid()); } - public function testGetValues() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); + /** + * @dataProvider dataVsValidProvider + */ + public function testDataVsValid( + $required, + $count, + $data, + $inputFilter, + $expectedRaw, + $expecteValues, + $expectedValid, + $expectedMessages + ) { + $this->inputFilter->setInputFilter($inputFilter); + $this->inputFilter->setData($data); + if ($count !== null) { + $this->inputFilter->setCount($count); } + $this->inputFilter->setIsRequired($required); - $expectedData = array( - array( - 'foo' => 'bazbat', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - 'foo' => 'bazbat', - 'bar' => '12345', - 'baz' => '', - ), - ), - array( - 'foo' => 'batbaz', - 'bar' => '54321', - 'baz' => '', - 'nest' => array( - 'foo' => 'batbaz', - 'bar' => '54321', - 'baz' => '', - ), - ) + $this->assertEquals( + $expectedValid, + $this->inputFilter->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->inputFilter->getMessages()) ); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($this->getValidCollectionData()); - - $this->assertTrue($this->filter->isValid()); - $this->assertEquals($expectedData, $this->filter->getValues()); - - $this->assertCount(2, $this->filter->getValidInput()); - foreach ($this->filter->getValidInput() as $validInputs) { - $this->assertCount(4, $validInputs); - } + $this->assertEquals($expectedRaw, $this->inputFilter->getRawValues(), 'getRawValues() value not match'); + $this->assertEquals($expecteValues, $this->inputFilter->getValues(), 'getValues() value not match'); + $this->assertEquals($expectedMessages, $this->inputFilter->getMessages(), 'getMessages() value not match'); } - public function testGetRawValues() + public function dataVsValidProvider() { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $expectedData = array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ), - array( - 'foo' => ' batbaz ', - 'bar' => '54321', - 'baz' => '', - 'nest' => array( - 'foo' => ' batbaz ', - 'bar' => '54321', - 'baz' => '', - ), - ) + $dataRaw = array( + 'fooInput' => 'fooRaw', ); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($this->getValidCollectionData()); - - $this->assertTrue($this->filter->isValid()); - $this->assertEquals($expectedData, $this->filter->getRawValues()); - } - - public function testGetMessagesForInvalidInputs() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $invalidCollectionData = array( - array( - 'foo' => ' bazbattoolong ', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ), - array( - 'foo' => ' bazbat ', - 'bar' => 'notstring', - 'baz' => '', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ), - array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - 'nest' => array( - // missing 'foo' here - 'bar' => '12345', - 'baz' => '', - ), - ), + $dataFiltered = array( + 'fooInput' => 'fooFiltered', ); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($invalidCollectionData); - - $this->assertFalse($this->filter->isValid()); - - $this->assertCount(3, $this->filter->getInvalidInput()); - foreach ($this->filter->getInvalidInput() as $invalidInputs) { - $this->assertCount(1, $invalidInputs); - } - - $messages = $this->filter->getMessages(); - - $this->assertCount(3, $messages); - $this->assertArrayHasKey('foo', $messages[0]); - $this->assertArrayHasKey('bar', $messages[1]); - $this->assertArrayHasKey('nest', $messages[2]); - - $this->assertCount(1, $messages[0]['foo']); - $this->assertCount(1, $messages[1]['bar']); - $this->assertCount(1, $messages[2]['nest']); - } - - public function testSetValidationGroupUsingFormStyle() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - // forms set an array of identical validation groups for each set of data - $formValidationGroup = array( - array( - 'foo', - 'bar', - ), - array( - 'foo', - 'bar', - ), - array( - 'foo', - 'bar', - ) + $colRaw = array($dataRaw); + $colFiltered = array($dataFiltered); + $errorMessage = array( + 'fooInput' => 'fooError', ); - - $data = array( - array( - 'foo' => ' bazbat ', - 'bar' => '12345' - ), - array( - 'foo' => ' batbaz ', - 'bar' => '54321' - ), - array( - 'foo' => ' batbaz ', - 'bar' => '54321' - ) + $colMessages = array($errorMessage); + + $self = $this; + $invalidIF = function () use ($dataRaw, $dataFiltered, $errorMessage, $self) { + return $self->createBaseInputFilterMock(false, $dataRaw, $dataFiltered, $errorMessage); + }; + $validIF = function () use ($dataRaw, $dataFiltered, $self) { + return $self->createBaseInputFilterMock(true, $dataRaw, $dataFiltered); + }; + $isRequired = true; + + // @codingStandardsIgnoreStart + $dataSets = array( + // Description => [$required, $count, $data, $inputFilter, $expectedRaw, $expecteValues, $expectedValid, $expectedMessages] + 'Required: T, Count: N, Valid: T' => array( $isRequired, null, $colRaw, $validIF , $colRaw, $colFiltered, true , array()), + 'Required: T, Count: N, Valid: F' => array( $isRequired, null, $colRaw, $invalidIF, $colRaw, $colFiltered, false, $colMessages), + 'Required: T, Count: +1, Valid: F' => array( $isRequired, 2, $colRaw, $invalidIF, $colRaw, $colFiltered, false, $colMessages), + 'Required: F, Count: N, Valid: T' => array(!$isRequired, null, $colRaw, $validIF , $colRaw, $colFiltered, true , array()), + 'Required: F, Count: N, Valid: F' => array(!$isRequired, null, $colRaw, $invalidIF, $colRaw, $colFiltered, false, $colMessages), + 'Required: F, Count: +1, Valid: F' => array(!$isRequired, 2, $colRaw, $invalidIF, $colRaw, $colFiltered, false, $colMessages), + 'Required: T, Data: array(), Valid: X' => array( $isRequired, null, array(), $invalidIF, array(), array(), false, array()), + 'Required: F, Data: array(), Valid: X' => array(!$isRequired, null, array(), $invalidIF, array(), array(), true , array()), ); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($data); - $this->filter->setValidationGroup($formValidationGroup); - - $this->assertTrue($this->filter->isValid()); - } - - public function testEmptyCollectionIsValidByDefault() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $data = array(); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($data); - - $this->assertTrue($this->filter->isValid()); - } - - public function testEmptyCollectionIsNotValidIfRequired() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('ext/intl not enabled'); - } - - $data = array(); - - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($data); - $this->filter->setIsRequired(true); - - $this->assertFalse($this->filter->isValid()); - } - - public function testSetRequired() - { - $this->filter->setIsRequired(true); - $this->assertEquals(true, $this->filter->getIsRequired()); - } - - public function testNonRequiredFieldsAreValidated() - { - $invalidCollectionData = array( - array( - 'foo' => ' bazbattoolong ', - 'bar' => '12345', - 'baz' => 'baztoolong', - 'nest' => array( - 'foo' => ' bazbat ', - 'bar' => '12345', - 'baz' => '', - ), - ) + // @codingStandardsIgnoreEnd + + array_walk( + $dataSets, + function (&$set) { + // Create unique mock input instances for each set + $inputFilter = $set[3](); + $set[3] = $inputFilter; + } ); - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($invalidCollectionData); - - $this->assertFalse($this->filter->isValid()); - $this->assertCount(2, current($this->filter->getInvalidInput())); - $this->assertArrayHasKey('baz', current($this->filter->getMessages())); + return $dataSets; } - public function testNestedCollectionWithEmptyChild() + public function testSetValidationGroupUsingFormStyle() { - $items_inputfilter = new BaseInputFilter(); - $items_inputfilter->add(new Input(), 'id') - ->add(new Input(), 'type'); - $items = new CollectionInputFilter(); - $items->setInputFilter($items_inputfilter); - - $groups_inputfilter = new BaseInputFilter(); - $groups_inputfilter->add(new Input(), 'group_class') - ->add($items, 'items'); - $groups = new CollectionInputFilter(); - $groups->setInputFilter($groups_inputfilter); - - $inputFilter = new BaseInputFilter(); - $inputFilter->add($groups, 'groups'); - - $preFilterdata = array( - 'groups' => array( - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 100, - 'type' => 'item-1', - ), - ), - ), - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 200, - 'type' => 'item-2', - ), - array( - 'id' => 300, - 'type' => 'item-3', - ), - array( - 'id' => 400, - 'type' => 'item-4', - ), - ), - ), - array( - 'group_class' => 'biz', - ), - ), + $validationGroup = array( + 'fooGroup', ); + $colValidationGroup = array($validationGroup); - $postFilterdata = array( - 'groups' => array( - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 100, - 'type' => 'item-1', - ), - ), - ), - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 200, - 'type' => 'item-2', - ), - array( - 'id' => 300, - 'type' => 'item-3', - ), - array( - 'id' => 400, - 'type' => 'item-4', - ), - ), - ), - array( - 'group_class' => 'biz', - 'items' => array(), - ), - ), + $dataRaw = array( + 'fooInput' => 'fooRaw', ); - $inputFilter->setData($preFilterdata); - $inputFilter->isValid(); - $values = $inputFilter->getValues(); - $this->assertEquals($postFilterdata, $values); - } - - public function testNestedCollectionWithEmptyData() - { - $items_inputfilter = new BaseInputFilter(); - $items_inputfilter->add(new Input(), 'id') - ->add(new Input(), 'type'); - $items = new CollectionInputFilter(); - $items->setInputFilter($items_inputfilter); - - $groups_inputfilter = new BaseInputFilter(); - $groups_inputfilter->add(new Input(), 'group_class') - ->add($items, 'items'); - $groups = new CollectionInputFilter(); - $groups->setInputFilter($groups_inputfilter); - - $inputFilter = new BaseInputFilter(); - $inputFilter->add($groups, 'groups'); - - $data = array( - 'groups' => array( - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 100, - 'type' => 'item-1', - ), - ), - ), - array( - 'group_class' => 'biz', - 'items' => array(), - ), - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 200, - 'type' => 'item-2', - ), - array( - 'id' => 300, - 'type' => 'item-3', - ), - array( - 'id' => 400, - 'type' => 'item-4', - ), - ), - ), - ), + $dataFiltered = array( + 'fooInput' => 'fooFiltered', ); - - $inputFilter->setData($data); - $inputFilter->isValid(); - $values = $inputFilter->getValues(); - $this->assertEquals($data, $values); - } - - /** - * @group 6472 - */ - public function testNestedCollectionWhereChildDataIsNotOverwritten() - { - $items_inputfilter = new BaseInputFilter(); - $items_inputfilter->add(new Input(), 'id') - ->add(new Input(), 'type'); - $items = new CollectionInputFilter(); - $items->setInputFilter($items_inputfilter); - - $groups_inputfilter = new BaseInputFilter(); - $groups_inputfilter->add(new Input(), 'group_class') - ->add($items, 'items'); - $groups = new CollectionInputFilter(); - $groups->setInputFilter($groups_inputfilter); - - $inputFilter = new BaseInputFilter(); - $inputFilter->add($groups, 'groups'); - - $data = array( - 'groups' => array( - array( - 'group_class' => 'bar', - 'items' => array( - array( - 'id' => 100, - 'type' => 'item-100', - ), - array( - 'id' => 101, - 'type' => 'item-101', - ), - array( - 'id' => 102, - 'type' => 'item-102', - ), - array( - 'id' => 103, - 'type' => 'item-103', - ), - ), - ), - array( - 'group_class' => 'foo', - 'items' => array( - array( - 'id' => 200, - 'type' => 'item-200', - ), - array( - 'id' => 201, - 'type' => 'item-201', - ), - ), - ), - ), + $colRaw = array($dataRaw); + $colFiltered = array($dataFiltered); + $baseInputFilter = $this->createBaseInputFilterMock(true, $dataRaw, $dataFiltered); + $baseInputFilter->expects($this->once()) + ->method('setValidationGroup') + ->with($validationGroup) + ; + + $this->inputFilter->setInputFilter($baseInputFilter); + $this->inputFilter->setData($colRaw); + $this->inputFilter->setValidationGroup($colValidationGroup); + + $this->assertTrue( + $this->inputFilter->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->inputFilter->getMessages()) ); - - $inputFilter->setData($data); - $inputFilter->isValid(); - $values = $inputFilter->getValues(); - $this->assertEquals($data, $values); + $this->assertEquals($colRaw, $this->inputFilter->getRawValues(), 'getRawValues() value not match'); + $this->assertEquals($colFiltered, $this->inputFilter->getValues(), 'getValues() value not match'); + $this->assertEquals(array(), $this->inputFilter->getMessages(), 'getMessages() value not match'); } public function dataNestingCollection() @@ -704,20 +233,24 @@ public function dataNestingCollection() return array( 'count not specified' => array( 'count' => null, - 'isValid' => true + 'isValid' => true, + ), + 'count=0' => array( + 'count' => 0, + 'isValid' => true, ), 'count = 1' => array( 'count' => 1, - 'isValid' => true + 'isValid' => true, ), 'count = 2' => array( 'count' => 2, - 'isValid' => false + 'isValid' => false, ), 'count = 3' => array( 'count' => 3, - 'isValid' => false - ) + 'isValid' => false, + ), ); } @@ -751,34 +284,116 @@ public function testNestingCollectionCountCached($count, $expectedIsValid) array( 'second_collection' => array( array( - 'input' => 'some value' + 'input' => 'some value', ), array( - 'input' => 'some value' - ) - ) + 'input' => 'some value', + ), + ), ), array( 'second_collection' => array( array( - 'input' => 'some value' + 'input' => 'some value', ), - ) - ) - ) + ), + ), + ), ); $mainInputFilter->setData($data); $this->assertSame($expectedIsValid, $mainInputFilter->isValid()); } - public function testInvalidCollectionIsNotValid() + public function inputFilterProvider() { - $data = 1; + $baseInputFilter = new BaseInputFilter(); + + $inputFilterSpecificationAsArray = array(); + $inputSpecificationAsTraversable = new ArrayIterator($inputFilterSpecificationAsArray); - $this->filter->setInputFilter($this->getBaseInputFilter()); - $this->filter->setData($data); + $inputFilterSpecificationResult = new InputFilter(); + $inputFilterSpecificationResult->getFactory()->getInputFilterManager(); + + $dataSets = array( + // Description => [inputFilter, $expectedType] + 'BaseInputFilter' => array($baseInputFilter, 'Zend\InputFilter\BaseInputFilter'), + 'array' => array($inputFilterSpecificationAsArray, 'Zend\InputFilter\InputFilter'), + 'Traversable' => array($inputSpecificationAsTraversable, 'Zend\InputFilter\InputFilter'), + ); + + return $dataSets; + } + + public function countVsDataProvider() + { + $data0 = array(); + $data1 = array('A' => 'a'); + $data2 = array('A' => 'a', 'B' => 'b'); + + // @codingStandardsIgnoreStart + return array( + // Description => [$count, $data, $expectedCount] + 'C: -1, D: null' => array( -1, null , 0), + 'C: 0, D: null' => array( 0, null , 0), + 'C: 1, D: null' => array( 1, null , 1), + 'C: null, D: 0' => array(null, $data0, 0), + 'C: null, D: 1' => array(null, $data1, 1), + 'C: null, D: 2' => array(null, $data2, 2), + 'C: -1, D: 0' => array( -1, $data0, 0), + 'C: 0, D: 0' => array( 0, $data0, 0), + 'C: 1, D: 0' => array( 1, $data0, 1), + 'C: -1, D: 1' => array( -1, $data1, 0), + 'C: 0, D: 1' => array( 0, $data1, 0), + 'C: 1, D: 1' => array( 1, $data1, 1), + ); + // @codingStandardsIgnoreEnd + } + + public function isRequiredProvider() + { + return array( + 'enabled' => array(true), + 'disabled' => array(false), + ); + } + + /** + * @param null|bool $isValid + * @param mixed[] $getRawValues + * @param mixed[] $getValues + * @param string[] $getMessages + * + * @return MockObject|BaseInputFilter + */ + public function createBaseInputFilterMock( + $isValid = null, + $getRawValues = array(), + $getValues = array(), + $getMessages = array() + ) { + /** @var BaseInputFilter|MockObject $inputFilter */ + $inputFilter = $this->getMock('Zend\InputFilter\BaseInputFilter'); + $inputFilter->method('getRawValues') + ->willReturn($getRawValues) + ; + $inputFilter->method('getValues') + ->willReturn($getValues) + ; + if (($isValid === false) || ($isValid === true)) { + $inputFilter->expects($this->once()) + ->method('isValid') + ->willReturn($isValid) + ; + } else { + $inputFilter->expects($this->never()) + ->method('isValid') + ; + } + $inputFilter->method('getMessages') + ->willReturn($getMessages) + ; - $this->assertFalse($this->filter->isValid()); + return $inputFilter; } } diff --git a/test/FactoryTest.php b/test/FactoryTest.php index 72f0427a..4ff44d9a 100644 --- a/test/FactoryTest.php +++ b/test/FactoryTest.php @@ -9,32 +9,261 @@ namespace ZendTest\InputFilter; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_TestCase as TestCase; use Zend\Filter; +use Zend\InputFilter\CollectionInputFilter; use Zend\InputFilter\Factory; use Zend\InputFilter\Input; use Zend\InputFilter\InputFilter; -use Zend\Validator; use Zend\InputFilter\InputFilterPluginManager; +use Zend\InputFilter\InputFilterProviderInterface; +use Zend\InputFilter\InputProviderInterface; use Zend\ServiceManager; +use Zend\Validator; +/** + * @covers Zend\InputFilter\Factory + */ class FactoryTest extends TestCase { + public function testCreateInputWithInvalidDataTypeThrowsInvalidArgumentException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects an array or Traversable; received "string"' + ); + /** @noinspection PhpParamsInspection */ + $factory->createInput('invalid_value'); + } + + public function testCreateInputWithTypeAsAnUnknownPluginAndNotExistsAsClassNameThrowException() + { + $factory = $this->createDefaultFactory(); + $type = 'foo'; + + /** @var InputFilterPluginManager|MockObject $pluginManager */ + $pluginManager = $this->getMock('Zend\InputFilter\InputFilterPluginManager'); + $pluginManager->expects($this->atLeastOnce()) + ->method('has') + ->with($type) + ->willReturn(false) + ; + $factory->setInputFilterManager($pluginManager); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Input factory expects the "type" to be a valid class or a plugin name; received "foo"' + ); + $factory->createInput( + array( + 'type' => $type, + ) + ); + } + + public function testCreateInputWithTypeAsAnInvalidPluginInstanceThrowException() + { + $factory = $this->createDefaultFactory(); + $type = 'fooPlugin'; + $pluginManager = $this->createInputFilterPluginManagerMockForPlugin($type, 'invalid_value'); + + $factory->setInputFilterManager($pluginManager); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Input factory expects the "type" to be a class implementing Zend\InputFilter\InputInterface; ' + . 'received "fooPlugin"' + ); + $factory->createInput( + array( + 'type' => $type, + ) + ); + } + + public function testCreateInputWithTypeAsAnInvalidClassInstanceThrowException() + { + $factory = $this->createDefaultFactory(); + $type = 'stdClass'; + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Input factory expects the "type" to be a class implementing Zend\InputFilter\InputInterface; ' . + 'received "stdClass"' + ); + $factory->createInput( + array( + 'type' => $type, + ) + ); + } + + public function testCreateInputWithFiltersAsAnInvalidTypeThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'expects the value associated with "filters" to be an array/Traversable of filters or filter specifications,' . + ' or a FilterChain; received "string"' + ); + $factory->createInput( + array( + 'filters' => 'invalid_value', + ) + ); + } + + public function testCreateInputWithFiltersAsAnSpecificationWithMissingNameThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Invalid filter specification provided; does not include "name" key' + ); + $factory->createInput( + array( + 'filters' => array( + array( + // empty + ) + ), + ) + ); + } + + public function testCreateInputWithFiltersAsAnCollectionOfInvalidTypesThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Invalid filter specification provided; was neither a filter instance nor an array specification' + ); + $factory->createInput( + array( + 'filters' => array( + 'invalid value' + ), + ) + ); + } + + public function testCreateInputWithValidatorsAsAnInvalidTypeThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'expects the value associated with "validators" to be an array/Traversable of validators or validator ' . + 'specifications, or a ValidatorChain; received "string"' + ); + $factory->createInput( + array( + 'validators' => 'invalid_value', + ) + ); + } + + public function testCreateInputWithValidatorsAsAnSpecificationWithMissingNameThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Invalid validator specification provided; does not include "name" key' + ); + $factory->createInput( + array( + 'validators' => array( + array( + // empty + ) + ), + ) + ); + } + + public function inputTypeSpecificationProvider() + { + return array( + // Description => [$specificationKey] + 'continue_if_empty' => array('continue_if_empty'), + 'fallback_value' => array('fallback_value'), + ); + } + + /** + * @dataProvider inputTypeSpecificationProvider + */ + public function testCreateInputWithSpecificInputTypeSettingsThrowException($specificationKey) + { + $factory = $this->createDefaultFactory(); + $type = 'pluginInputInterface'; + + $pluginManager = $this->createInputFilterPluginManagerMockForPlugin($type, $this->getMock('Zend\InputFilter\InputInterface')); + $factory->setInputFilterManager($pluginManager); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + sprintf('"%s" can only set to inputs of type "Zend\InputFilter\Input"', $specificationKey) + ); + $factory->createInput( + array( + 'type' => $type, + $specificationKey => true, + ) + ); + } + + public function testCreateInputWithValidatorsAsAnCollectionOfInvalidTypesThrowException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'Invalid validator specification provided; was neither a validator instance nor an array specification' + ); + $factory->createInput( + array( + 'validators' => array( + 'invalid value' + ), + ) + ); + } + + public function testCreateInputFilterWithInvalidDataTypeThrowsInvalidArgumentException() + { + $factory = $this->createDefaultFactory(); + + $this->setExpectedException( + 'Zend\InputFilter\Exception\InvalidArgumentException', + 'expects an array or Traversable; received "string"' + ); + /** @noinspection PhpParamsInspection */ + $factory->createInputFilter('invalid_value'); + } + public function testFactoryComposesFilterChainByDefault() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $this->assertInstanceOf('Zend\Filter\FilterChain', $factory->getDefaultFilterChain()); } public function testFactoryComposesValidatorChainByDefault() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $this->assertInstanceOf('Zend\Validator\ValidatorChain', $factory->getDefaultValidatorChain()); } public function testFactoryAllowsInjectingFilterChain() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $filterChain = new Filter\FilterChain(); $factory->setDefaultFilterChain($filterChain); $this->assertSame($filterChain, $factory->getDefaultFilterChain()); @@ -42,7 +271,7 @@ public function testFactoryAllowsInjectingFilterChain() public function testFactoryAllowsInjectingValidatorChain() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $validatorChain = new Validator\ValidatorChain(); $factory->setDefaultValidatorChain($validatorChain); $this->assertSame($validatorChain, $factory->getDefaultValidatorChain()); @@ -50,7 +279,7 @@ public function testFactoryAllowsInjectingValidatorChain() public function testFactoryUsesComposedFilterChainWhenCreatingNewInputObjects() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $filterChain = new Filter\FilterChain(); $pluginManager = new Filter\FilterPluginManager(); $filterChain->setPluginManager($pluginManager); @@ -66,7 +295,7 @@ public function testFactoryUsesComposedFilterChainWhenCreatingNewInputObjects() public function testFactoryUsesComposedValidatorChainWhenCreatingNewInputObjects() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $validatorChain = new Validator\ValidatorChain(); $validatorPlugins = new Validator\ValidatorPluginManager(); $validatorChain->setPluginManager($validatorPlugins); @@ -82,7 +311,7 @@ public function testFactoryUsesComposedValidatorChainWhenCreatingNewInputObjects public function testFactoryInjectsComposedFilterAndValidatorChainsIntoInputObjectsWhenCreatingNewInputFilterObjects() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $filterPlugins = new Filter\FilterPluginManager(); $validatorPlugins = new Validator\ValidatorPluginManager(); $filterChain = new Filter\FilterChain(); @@ -107,9 +336,12 @@ public function testFactoryInjectsComposedFilterAndValidatorChainsIntoInputObjec $this->assertSame($validatorPlugins, $inputValidatorChain->getPluginManager()); } + /** + * @requires extension mbstring + */ public function testFactoryWillCreateInputWithSuggestedFilters() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $htmlEntities = new Filter\HtmlEntities(); $input = $factory->createInput(array( 'name' => 'foo', @@ -151,7 +383,7 @@ public function testFactoryWillCreateInputWithSuggestedFilters() public function testFactoryWillCreateInputWithSuggestedValidators() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $digits = new Validator\Digits(); $input = $factory->createInput(array( 'name' => 'foo', @@ -195,7 +427,7 @@ public function testFactoryWillCreateInputWithSuggestedValidators() public function testFactoryWillCreateInputWithSuggestedRequiredFlagAndAlternativeAllowEmptyFlag() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', 'required' => false, @@ -208,7 +440,7 @@ public function testFactoryWillCreateInputWithSuggestedRequiredFlagAndAlternativ public function testFactoryWillCreateInputWithSuggestedAllowEmptyFlagAndImpliesRequiredFlag() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', 'allow_empty' => true, @@ -220,7 +452,7 @@ public function testFactoryWillCreateInputWithSuggestedAllowEmptyFlagAndImpliesR public function testFactoryWillCreateInputWithSuggestedName() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', )); @@ -230,7 +462,7 @@ public function testFactoryWillCreateInputWithSuggestedName() public function testFactoryWillCreateInputWithContinueIfEmptyFlag() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', 'continue_if_empty' => true, @@ -241,7 +473,7 @@ public function testFactoryWillCreateInputWithContinueIfEmptyFlag() public function testFactoryAcceptsInputInterface() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = new Input(); $inputFilter = $factory->createInputFilter(array( @@ -255,7 +487,7 @@ public function testFactoryAcceptsInputInterface() public function testFactoryAcceptsInputFilterInterface() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = new InputFilter(); $inputFilter = $factory->createInputFilter(array( @@ -269,7 +501,7 @@ public function testFactoryAcceptsInputFilterInterface() public function testFactoryWillCreateInputFilterAndAllInputObjectsFromGivenConfiguration() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $inputFilter = $factory->createInputFilter(array( 'foo' => array( 'name' => 'foo', @@ -385,7 +617,7 @@ public function testFactoryWillCreateInputFilterAndAllInputObjectsFromGivenConfi public function testFactoryWillCreateInputFilterMatchingInputNameWhenNotSpecified() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $inputFilter = $factory->createInputFilter(array( array('name' => 'foo') )); @@ -396,7 +628,7 @@ public function testFactoryWillCreateInputFilterMatchingInputNameWhenNotSpecifie public function testFactoryAllowsPassingValidatorChainsInInputSpec() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $chain = new Validator\ValidatorChain(); $input = $factory->createInput(array( 'name' => 'foo', @@ -409,7 +641,7 @@ public function testFactoryAllowsPassingValidatorChainsInInputSpec() public function testFactoryAllowsPassingFilterChainsInInputSpec() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $chain = new Filter\FilterChain(); $input = $factory->createInput(array( 'name' => 'foo', @@ -422,8 +654,9 @@ public function testFactoryAllowsPassingFilterChainsInInputSpec() public function testFactoryAcceptsCollectionInputFilter() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); + /** @var CollectionInputFilter $inputFilter */ $inputFilter = $factory->createInputFilter(array( 'type' => 'Zend\InputFilter\CollectionInputFilter', 'required' => true, @@ -439,7 +672,7 @@ public function testFactoryAcceptsCollectionInputFilter() public function testFactoryWillCreateInputWithErrorMessage() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', 'error_message' => 'My custom error message', @@ -447,20 +680,23 @@ public function testFactoryWillCreateInputWithErrorMessage() $this->assertEquals('My custom error message', $input->getErrorMessage()); } + /** + * @requires extension mbstring + */ public function testFactoryWillNotGetPrioritySetting() { //Reminder: Priority at which to enqueue filter; defaults to 1000 (higher executes earlier) - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'foo', 'filters' => array( array( 'name' => 'string_trim', - 'priority' => \Zend\Filter\FilterChain::DEFAULT_PRIORITY - 1 // 999 + 'priority' => Filter\FilterChain::DEFAULT_PRIORITY - 1 // 999 ), array( 'name' => 'string_to_upper', - 'priority' => \Zend\Filter\FilterChain::DEFAULT_PRIORITY + 1 //1001 + 'priority' => Filter\FilterChain::DEFAULT_PRIORITY + 1 //1001 ), array( 'name' => 'string_to_lower', // default priority 1000 @@ -492,7 +728,7 @@ public function testFactoryWillNotGetPrioritySetting() public function testConflictNameWithInputFilterType() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $inputFilter = $factory->createInputFilter( array( @@ -509,6 +745,7 @@ public function testConflictNameWithInputFilterType() public function testCustomFactoryInCollection() { $factory = new TestAsset\CustomFactory(); + /** @var CollectionInputFilter $inputFilter */ $inputFilter = $factory->createInputFilter(array( 'type' => 'collection', 'input_filter' => new InputFilter(), @@ -521,7 +758,7 @@ public function testCustomFactoryInCollection() */ public function testCanSetInputErrorMessage() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput(array( 'name' => 'test', 'type' => 'Zend\InputFilter\Input', @@ -537,7 +774,7 @@ public function testSetInputFilterManagerWithServiceManager() $serviceManager->setService('ValidatorManager', new Validator\ValidatorPluginManager); $serviceManager->setService('FilterManager', new Filter\FilterPluginManager); $inputFilterManager->setServiceLocator($serviceManager); - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $factory->setInputFilterManager($inputFilterManager); $this->assertInstanceOf( 'Zend\Validator\ValidatorPluginManager', @@ -552,7 +789,7 @@ public function testSetInputFilterManagerWithServiceManager() public function testSetInputFilterManagerWithoutServiceManager() { $inputFilterManager = new InputFilterPluginManager(); - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $factory->setInputFilterManager($inputFilterManager); $this->assertSame($inputFilterManager, $factory->getInputFilterManager()); } @@ -571,7 +808,7 @@ public function testSetInputFilterManagerOnConstruct() */ public function testSetsBreakChainOnFailure() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $this->assertTrue($factory->createInput(array('break_on_failure' => true))->breakOnFailure()); @@ -580,7 +817,7 @@ public function testSetsBreakChainOnFailure() public function testCanCreateInputFilterWithNullInputs() { - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $inputFilter = $factory->createInputFilter(array( 'foo' => array( @@ -604,7 +841,7 @@ public function testCanCreateInputFilterWithNullInputs() */ public function testCanCreateInputFromProvider() { - /* @group $provider \Zend\InputFilter\InputProviderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var InputProviderInterface|MockObject $provider */ $provider = $this->getMock('Zend\InputFilter\InputProviderInterface', array('getInputSpecification')); $provider @@ -612,7 +849,7 @@ public function testCanCreateInputFromProvider() ->method('getInputSpecification') ->will($this->returnValue(array('name' => 'foo'))); - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $input = $factory->createInput($provider); $this->assertInstanceOf('Zend\InputFilter\InputInterface', $input); @@ -623,7 +860,7 @@ public function testCanCreateInputFromProvider() */ public function testCanCreateInputFilterFromProvider() { - /* @group $provider \Zend\InputFilter\InputFilterProviderInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var InputFilterProviderInterface|MockObject $provider */ $provider = $this->getMock( 'Zend\InputFilter\InputFilterProviderInterface', array('getInputFilterSpecification') @@ -642,9 +879,72 @@ public function testCanCreateInputFilterFromProvider() ), ))); - $factory = new Factory(); + $factory = $this->createDefaultFactory(); $inputFilter = $factory->createInputFilter($provider); $this->assertInstanceOf('Zend\InputFilter\InputFilterInterface', $inputFilter); } + + public function testSuggestedTypeMayBePluginNameInInputFilterPluginManager() + { + $factory = $this->createDefaultFactory(); + $pluginManager = new InputFilterPluginManager(); + $pluginManager->setService('bar', new Input('bar')); + $factory->setInputFilterManager($pluginManager); + + $input = $factory->createInput(array( + 'type' => 'bar' + )); + $this->assertSame('bar', $input->getName()); + } + + public function testInputFromPluginManagerMayBeFurtherConfiguredWithSpec() + { + $factory = $this->createDefaultFactory(); + $pluginManager = new InputFilterPluginManager(); + $pluginManager->setService('bar', $barInput = new Input('bar')); + $this->assertTrue($barInput->isRequired()); + $factory->setInputFilterManager($pluginManager); + + $input = $factory->createInput(array( + 'type' => 'bar', + 'required' => false + )); + + $this->assertFalse($input->isRequired()); + $this->assertSame('bar', $input->getName()); + } + + /** + * @return Factory + */ + protected function createDefaultFactory() + { + $factory = new Factory(); + + return $factory; + } + + /** + * @param string $pluginName + * @param mixed $pluginValue + * + * @return MockObject|InputFilterPluginManager + */ + protected function createInputFilterPluginManagerMockForPlugin($pluginName, $pluginValue) + { + /** @var InputFilterPluginManager|MockObject $pluginManager */ + $pluginManager = $this->getMock('Zend\InputFilter\InputFilterPluginManager'); + $pluginManager->expects($this->atLeastOnce()) + ->method('has') + ->with($pluginName) + ->willReturn(true) + ; + $pluginManager->expects($this->atLeastOnce()) + ->method('get') + ->with($pluginName) + ->willReturn($pluginValue) + ; + return $pluginManager; + } } diff --git a/test/FileInputTest.php b/test/FileInputTest.php index 27888b4c..73e654ef 100644 --- a/test/FileInputTest.php +++ b/test/FileInputTest.php @@ -9,12 +9,19 @@ namespace ZendTest\InputFilter; -use Zend\InputFilter\FileInput; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use Zend\Filter; +use Zend\InputFilter\FileInput; use Zend\Validator; +/** + * @covers Zend\InputFilter\FileInput + */ class FileInputTest extends InputTest { + /** @var FileInput */ + protected $input; + public function setUp() { $this->input = new FileInput('foo'); @@ -22,13 +29,6 @@ public function setUp() $this->input->setAutoPrependUploadValidator(false); } - public function testValueMayBeInjected() - { - $value = array('tmp_name' => 'bar'); - $this->input->setValue($value); - $this->assertEquals($value, $this->input->getValue()); - } - public function testRetrievingValueFiltersTheValue() { $this->markTestSkipped('Test are not enabled in FileInputTest'); @@ -40,6 +40,7 @@ public function testRetrievingValueFiltersTheValueOnlyAfterValidating() $this->input->setValue($value); $newValue = array('tmp_name' => 'foo'); + /** @var Filter\File\Rename|MockObject $filterMock */ $filterMock = $this->getMockBuilder('Zend\Filter\File\Rename') ->disableOriginalConstructor() ->getMock(); @@ -57,7 +58,10 @@ function ($value) use ($filterMock) { ); $this->assertEquals($value, $this->input->getValue()); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); $this->assertEquals($newValue, $this->input->getValue()); } @@ -71,6 +75,7 @@ public function testCanFilterArrayOfMultiFileData() $this->input->setValue($values); $newValue = array('tmp_name' => 'new'); + /** @var Filter\File\Rename|MockObject $filterMock */ $filterMock = $this->getMockBuilder('Zend\Filter\File\Rename') ->disableOriginalConstructor() ->getMock(); @@ -88,7 +93,10 @@ function ($value) use ($filterMock) { ); $this->assertEquals($values, $this->input->getValue()); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); $this->assertEquals( array($newValue, $newValue, $newValue), $this->input->getValue() @@ -104,22 +112,6 @@ public function testCanRetrieveRawValue() $this->assertEquals($value, $this->input->getRawValue()); } - public function testIsValidReturnsFalseIfValidationChainFails() - { - $this->input->setValue(array('tmp_name' => 'bar')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - } - - public function testIsValidReturnsTrueIfValidationChainSucceeds() - { - $this->input->setValue(array('tmp_name' => 'bar')); - $validator = new Validator\NotEmpty(); - $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); - } - public function testValidationOperatesOnFilteredValue() { $this->markTestSkipped('Test is not enabled in FileInputTest'); @@ -136,6 +128,7 @@ public function testValidationOperatesBeforeFiltering() $this->input->setValue($badValue); $filteredValue = array('tmp_name' => 'new'); + /** @var Filter\File\Rename|MockObject $filterMock */ $filterMock = $this->getMockBuilder('Zend\Filter\File\Rename') ->disableOriginalConstructor() ->getMock(); @@ -164,7 +157,10 @@ function ($value) use ($filterMock) { 'error' => 0, ); $this->input->setValue($goodValue); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); $this->assertEquals($filteredValue, $this->input->getValue()); } @@ -201,7 +197,10 @@ public function testCanValidateArrayOfMultiFileData() $this->input->setValue($values); $validator = new Validator\File\Exists(); $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); // Negative test $values[1]['tmp_name'] = 'file-not-found'; @@ -209,18 +208,6 @@ public function testCanValidateArrayOfMultiFileData() $this->assertFalse($this->input->isValid()); } - public function testSpecifyingMessagesToInputReturnsThoseOnFailedValidation() - { - $this->input->setValue(array('tmp_name' => 'bar')); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->input->setErrorMessage('Please enter only digits'); - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayNotHasKey(Validator\Digits::NOT_DIGITS, $messages); - $this->assertContains('Please enter only digits', $messages); - } - public function testAutoPrependUploadValidatorIsOnByDefault() { $input = new FileInput('foo'); @@ -255,7 +242,10 @@ public function testUploadValidatorIsNotAddedWhenIsValidIsCalled() $validatorChain = $this->input->getValidatorChain(); $this->assertEquals(0, count($validatorChain->getValidators())); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); $this->assertEquals(0, count($validatorChain->getValidators())); } @@ -266,6 +256,7 @@ public function testRequiredUploadValidatorValidatorNotAddedWhenOneExists() $this->assertTrue($this->input->isRequired()); $this->input->setValue(array('tmp_name' => 'bar')); + /** @var Validator\File\UploadFile|MockObject $uploadMock */ $uploadMock = $this->getMock('Zend\Validator\File\UploadFile', array('isValid')); $uploadMock->expects($this->exactly(1)) ->method('isValid') @@ -273,7 +264,10 @@ public function testRequiredUploadValidatorValidatorNotAddedWhenOneExists() $validatorChain = $this->input->getValidatorChain(); $validatorChain->prependValidator($uploadMock); - $this->assertTrue($this->input->isValid()); + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); $validators = $validatorChain->getValidators(); $this->assertEquals(1, count($validators)); @@ -287,6 +281,7 @@ public function testValidationsRunWithoutFileArrayDueToAjaxPost() $this->assertTrue($this->input->isRequired()); $this->input->setValue(''); + /** @var Validator\File\UploadFile|MockObject $uploadMock */ $uploadMock = $this->getMock('Zend\Validator\File\UploadFile', array('isValid')); $uploadMock->expects($this->exactly(1)) ->method('isValid') @@ -301,48 +296,31 @@ public function testValidationsRunWithoutFileArrayDueToAjaxPost() $this->assertEquals($uploadMock, $validators[0]['instance']); } - public function testNotEmptyValidatorAddedWhenIsValidIsCalled() + public function testNotEmptyValidatorAddedWhenIsValidIsCalled($value = null) { $this->markTestSkipped('Test is not enabled in FileInputTest'); } - public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() + public function testRequiredNotEmptyValidatorNotAddedWhenOneExists($value = null) { $this->markTestSkipped('Test is not enabled in FileInputTest'); } - public function testMerge() - { - $value = array('tmp_name' => 'bar'); - - $input = new FileInput('foo'); - $input->setAutoPrependUploadValidator(false); - $input->setValue($value); - $filter = new Filter\StringTrim(); - $input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $input->getValidatorChain()->attach($validator); - - $input2 = new FileInput('bar'); - $input2->merge($input); - $validatorChain = $input->getValidatorChain(); - $filterChain = $input->getFilterChain(); - - $this->assertFalse($input2->getAutoPrependUploadValidator()); - $this->assertEquals($value, $input2->getRawValue()); - $this->assertEquals(1, $validatorChain->count()); - $this->assertEquals(1, $filterChain->count()); - - $validators = $validatorChain->getValidators(); - $this->assertInstanceOf('Zend\Validator\Digits', $validators[0]['instance']); - - $filters = $filterChain->getFilters()->toArray(); - $this->assertInstanceOf('Zend\Filter\StringTrim', $filters[0]); + public function testFallbackValueVsIsValidRules( + $required = null, + $fallbackValue = null, + $originalValue = null, + $isValid = null, + $expectedValue = null + ) { + $this->markTestSkipped('Input::setFallbackValue is not implemented on FileInput'); } - public function testFallbackValue($fallbackValue = null) - { - $this->markTestSkipped('Not use fallback value'); + public function testFallbackValueVsIsValidRulesWhenValueNotSet( + $required = null, + $fallbackValue = null + ) { + $this->markTestSkipped('Input::setFallbackValue is not implemented on FileInput'); } public function testIsEmptyFileNotArray() @@ -393,58 +371,100 @@ public function testIsEmptyFileMultiFileOk() $this->assertFalse($this->input->isEmptyFile($rawValue)); } - public function emptyValuesProvider() + /** + * Specific FileInput::merge extras + */ + public function testFileInputMerge() { - // Provide empty values specific for file input - return array( - array('file'), - array(array( - 'tmp_name' => '', - 'error' => \UPLOAD_ERR_NO_FILE, - )), - array(array(array( - 'tmp_name' => 'foo', - 'error' => \UPLOAD_ERR_NO_FILE - ))), + $source = new FileInput(); + $source->setAutoPrependUploadValidator(true); + + $target = $this->input; + $target->setAutoPrependUploadValidator(false); + + $return = $target->merge($source); + $this->assertSame($target, $return, 'merge() must return it self'); + + $this->assertEquals( + true, + $target->getAutoPrependUploadValidator(), + 'getAutoPrependUploadValidator() value not match' ); } - /** - * @dataProvider emptyValuesProvider - */ - public function testAllowEmptyOptionSet($emptyValue) + public function isRequiredVsAllowEmptyVsContinueIfEmptyVsIsValidProvider() { - // UploadFile validator is disabled, pretend one - $validator = new Validator\Callback(function () { - return false; // This should never be called - }); - $this->input->getValidatorChain()->attach($validator); - parent::testAllowEmptyOptionSet($emptyValue); + $dataSets = parent::isRequiredVsAllowEmptyVsContinueIfEmptyVsIsValidProvider(); + + // FileInput do not use NotEmpty validator so the only validator present in the chain is the custom one. + unset($dataSets['Required: T; AEmpty: F; CIEmpty: F; Validator: X, Value: Empty / tmp_name']); + unset($dataSets['Required: T; AEmpty: F; CIEmpty: F; Validator: X, Value: Empty / single']); + unset($dataSets['Required: T; AEmpty: F; CIEmpty: F; Validator: X, Value: Empty / multi']); + + return $dataSets; } - /** - * @dataProvider emptyValuesProvider - */ - public function testAllowEmptyOptionNotSet($emptyValue) + public function emptyValueProvider() { - // UploadFile validator is disabled, pretend one - $message = 'pretend failing UploadFile validator'; - $validator = new Validator\Callback(function () { - return false; - }); - $validator->setMessage($message); - $this->input->getValidatorChain()->attach($validator); - parent::testAllowEmptyOptionNotSet($emptyValue); - $this->assertEquals(array('callbackValue' => $message), $this->input->getMessages()); + return array( + 'tmp_name' => array( + 'raw' => 'file', + 'filtered' => array( + 'tmp_name' => 'file', + 'name' => 'file', + 'size' => 0, + 'type' => '', + 'error' => UPLOAD_ERR_NO_FILE, + ), + ), + 'single' => array( + 'raw' => array( + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + ), + 'filtered' => array( + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + ), + ), + 'multi' => array( + 'raw' => array( + array( + 'tmp_name' => 'foo', + 'error' => UPLOAD_ERR_NO_FILE, + ), + ), + 'filtered' => array( + 'tmp_name' => 'foo', + 'error' => UPLOAD_ERR_NO_FILE, + ), + ), + ); } - public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() + public function mixedValueProvider() { - $this->markTestSkipped('does not apply to FileInput'); + $fooUploadErrOk = array( + 'tmp_name' => 'foo', + 'error' => UPLOAD_ERR_OK, + ); + + return array( + 'single' => array( + 'raw' => $fooUploadErrOk, + 'filtered' => $fooUploadErrOk, + ), + 'multi' => array( + 'raw' => array( + $fooUploadErrOk, + ), + 'filtered' => $fooUploadErrOk, + ), + ); } - public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() + public function getDummyValue($raw = true) { - $this->markTestSkipped('does not apply to FileInput'); + return array('tmp_name' => 'bar'); } } diff --git a/test/InputFilterAbstractServiceFactoryTest.php b/test/InputFilterAbstractServiceFactoryTest.php index 0646044d..9cc2c55d 100644 --- a/test/InputFilterAbstractServiceFactoryTest.php +++ b/test/InputFilterAbstractServiceFactoryTest.php @@ -9,15 +9,29 @@ namespace ZendTest\InputFilter; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_TestCase as TestCase; use Zend\Filter\FilterPluginManager; +use Zend\InputFilter\InputFilterAbstractServiceFactory; use Zend\InputFilter\InputFilterPluginManager; use Zend\ServiceManager\ServiceManager; +use Zend\Validator\ValidatorInterface; use Zend\Validator\ValidatorPluginManager; -use Zend\InputFilter\InputFilterAbstractServiceFactory; +/** + * @covers Zend\InputFilter\InputFilterAbstractServiceFactory + */ class InputFilterAbstractServiceFactoryTest extends TestCase { + /** @var ServiceManager */ + protected $services; + + /** @var InputFilterPluginManager */ + protected $filters; + + /** @var InputFilterAbstractServiceFactory */ + protected $factory; + public function setUp() { $this->services = new ServiceManager(); @@ -79,6 +93,7 @@ public function testUsesConfiguredValidationAndFilterManagerServicesWhenCreating $filters->setService('foo', $filter); $validators = new ValidatorPluginManager(); + /** @var ValidatorInterface|MockObject $validator */ $validator = $this->getMock('Zend\Validator\ValidatorInterface'); $validators->setService('foo', $validator); @@ -127,6 +142,7 @@ public function testRetrieveInputFilterFromInputFilterPluginManager() $filters->setService('foo', $filter); $validators = new ValidatorPluginManager(); + /** @var ValidatorInterface|MockObject $validator */ $validator = $this->getMock('Zend\Validator\ValidatorInterface'); $validators->setService('foo', $validator); diff --git a/test/InputFilterAwareTraitTest.php b/test/InputFilterAwareTraitTest.php index d4cc35e1..25dc945d 100644 --- a/test/InputFilterAwareTraitTest.php +++ b/test/InputFilterAwareTraitTest.php @@ -14,6 +14,7 @@ /** * @requires PHP 5.4 + * @covers Zend\InputFilter\InputFilterAwareTrait */ class InputFilterAwareTraitTest extends TestCase { diff --git a/test/InputFilterManagerTest.php b/test/InputFilterManagerTest.php deleted file mode 100644 index a602e534..00000000 --- a/test/InputFilterManagerTest.php +++ /dev/null @@ -1,61 +0,0 @@ -manager = new InputFilterPluginManager(); - } - - public function testRegisteringInvalidElementRaisesException() - { - $this->setExpectedException('Zend\InputFilter\Exception\RuntimeException'); - $this->manager->setService('test', $this); - } - - public function testLoadingInvalidElementRaisesException() - { - $this->manager->setInvokableClass('test', get_class($this)); - $this->setExpectedException('Zend\InputFilter\Exception\RuntimeException'); - $this->manager->get('test'); - } - - /** - * @covers Zend\InputFilter\InputFilterPluginManager::validatePlugin - */ - public function testAllowLoadingInstancesOfInputFilterInterface() - { - $inputFilter = $this->getMock('Zend\InputFilter\InputFilterInterface'); - - $this->assertNull($this->manager->validatePlugin($inputFilter)); - } - - /** - * @covers Zend\InputFilter\InputFilterPluginManager::validatePlugin - */ - public function testAllowLoadingInstancesOfInputInterface() - { - $input = $this->getMock('Zend\InputFilter\InputInterface'); - - $this->assertNull($this->manager->validatePlugin($input)); - } -} diff --git a/test/InputFilterPluginManagerTest.php b/test/InputFilterPluginManagerTest.php new file mode 100644 index 00000000..996e5c36 --- /dev/null +++ b/test/InputFilterPluginManagerTest.php @@ -0,0 +1,208 @@ +manager = new InputFilterPluginManager(); + } + + public function testIsASubclassOfAbstractPluginManager() + { + $this->assertInstanceOf('Zend\ServiceManager\AbstractPluginManager', $this->manager); + } + + public function testIsNotSharedByDefault() + { + $this->assertFalse($this->manager->shareByDefault()); + } + + public function testRegisteringInvalidElementRaisesException() + { + $this->setExpectedException( + 'Zend\InputFilter\Exception\RuntimeException', + 'must implement Zend\InputFilter\InputFilterInterface or Zend\InputFilter\InputInterface' + ); + $this->manager->setService('test', $this); + } + + public function testLoadingInvalidElementRaisesException() + { + $this->manager->setInvokableClass('test', get_class($this)); + $this->setExpectedException('Zend\InputFilter\Exception\RuntimeException'); + $this->manager->get('test'); + } + + public function defaultInvokableClassesProvider() + { + return array( + // Description => [$alias, $expectedInstance] + 'inputfilter' => array('inputfilter', 'Zend\InputFilter\InputFilter'), + 'collection' => array('collection', 'Zend\InputFilter\CollectionInputFilter'), + ); + } + + /** + * @dataProvider defaultInvokableClassesProvider + */ + public function testDefaultInvokableClasses($alias, $expectedInstance) + { + $service = $this->manager->get($alias); + + $this->assertInstanceOf($expectedInstance, $service, 'get() return type not match'); + } + + public function testInputFilterInvokableClassSMDependenciesArePopulatedWithoutServiceLocator() + { + $this->assertNull($this->manager->getServiceLocator(), 'Plugin manager is expected to no have a service locator'); + + /** @var InputFilter $service */ + $service = $this->manager->get('inputfilter'); + + $factory = $service->getFactory(); + $this->assertSame( + $this->manager, + $factory->getInputFilterManager(), + 'Factory::getInputFilterManager() is not populated with the expected plugin manager' + ); + } + + public function testInputFilterInvokableClassSMDependenciesArePopulatedWithServiceLocator() + { + $filterManager = $this->getMock('Zend\Filter\FilterPluginManager'); + $validatorManager = $this->getMock('Zend\Validator\ValidatorPluginManager'); + + $serviceLocator = $this->createServiceLocatorInterfaceMock(); + $serviceLocator->method('get') + ->willReturnMap( + array( + array('FilterManager', $filterManager), + array('ValidatorManager', $validatorManager), + ) + ) + ; + + $this->manager->setServiceLocator($serviceLocator); + $this->assertSame($serviceLocator, $this->manager->getServiceLocator(), 'getServiceLocator() value not match'); + + /** @var InputFilter $service */ + $service = $this->manager->get('inputfilter'); + + $factory = $service->getFactory(); + $this->assertSame( + $this->manager, + $factory->getInputFilterManager(), + 'Factory::getInputFilterManager() is not populated with the expected plugin manager' + ); + + $defaultFilterChain = $factory->getDefaultFilterChain(); + $this->assertSame( + $filterManager, + $defaultFilterChain->getPluginManager(), + 'Factory::getDefaultFilterChain() is not populated with the expected plugin manager' + ); + + $defaultValidatorChain = $factory->getDefaultValidatorChain(); + $this->assertSame( + $validatorManager, + $defaultValidatorChain->getPluginManager(), + 'Factory::getDefaultValidatorChain() is not populated with the expected plugin manager' + ); + } + + public function serviceProvider() + { + $inputFilterInterfaceMock = $this->createInputFilterInterfaceMock(); + $inputInterfaceMock = $this->createInputInterfaceMock(); + + // @formatter:off + return array( + // Description => [$serviceName, $service, $instanceOf] + 'InputFilterInterface' => array('inputFilterInterfaceService', $inputFilterInterfaceMock, 'Zend\InputFilter\InputFilterInterface'), + 'InputInterface' => array('inputInterfaceService', $inputInterfaceMock, 'Zend\InputFilter\InputInterface'), + ); + // @formatter:on + } + + /** + * @dataProvider serviceProvider + */ + public function testGet($serviceName, $service) + { + $this->manager->setService($serviceName, $service); + + $this->assertSame($service, $this->manager->get($serviceName), 'get() value not match'); + } + + /** + * @dataProvider serviceProvider + */ + public function testServicesAreInitiatedIfImplementsInitializableInterface($serviceName, $service, $instanceOf) + { + $initializableProphecy = $this->prophesize($instanceOf)->willImplement('Zend\Stdlib\InitializableInterface'); + $service = $initializableProphecy->reveal(); + + $this->manager->setService($serviceName, $service); + $this->assertSame($service, $this->manager->get($serviceName), 'get() value not match'); + + /** @noinspection PhpUndefinedMethodInspection */ + $initializableProphecy->init()->shouldBeCalled(); + } + + /** + * @return MockObject|InputFilterInterface + */ + protected function createInputFilterInterfaceMock() + { + /** @var InputFilterInterface|MockObject $inputFilter */ + $inputFilter = $this->getMock('Zend\InputFilter\InputFilterInterface'); + + return $inputFilter; + } + + /** + * @return MockObject|InputInterface + */ + protected function createInputInterfaceMock() + { + /** @var InputInterface|MockObject $input */ + $input = $this->getMock('Zend\InputFilter\InputInterface'); + + return $input; + } + + /** + * @return MockObject|ServiceLocatorInterface + */ + protected function createServiceLocatorInterfaceMock() + { + /** @var ServiceLocatorInterface|MockObject $serviceLocator */ + $serviceLocator = $this->getMock('Zend\ServiceManager\ServiceLocatorInterface'); + + return $serviceLocator; + } +} diff --git a/test/InputFilterTest.php b/test/InputFilterTest.php index 99a16dec..6ee46229 100644 --- a/test/InputFilterTest.php +++ b/test/InputFilterTest.php @@ -9,110 +9,73 @@ namespace ZendTest\InputFilter; -use PHPUnit_Framework_TestCase as TestCase; -use Zend\Filter; -use Zend\InputFilter\CollectionInputFilter; +use ArrayIterator; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use Zend\InputFilter\Factory; use Zend\InputFilter\Input; use Zend\InputFilter\InputFilter; -class InputFilterTest extends TestCase +/** + * @covers Zend\InputFilter\InputFilter + */ +class InputFilterTest extends BaseInputFilterTest { /** * @var InputFilter */ - protected $filter; + protected $inputFilter; public function setUp() { - $this->filter = new InputFilter(); + $this->inputFilter = new InputFilter(); } public function testLazilyComposesAFactoryByDefault() { - $factory = $this->filter->getFactory(); + $factory = $this->inputFilter->getFactory(); $this->assertInstanceOf('Zend\InputFilter\Factory', $factory); } public function testCanComposeAFactory() { - $factory = new Factory(); - $this->filter->setFactory($factory); - $this->assertSame($factory, $this->filter->getFactory()); + $factory = $this->createFactoryMock(); + $this->inputFilter->setFactory($factory); + $this->assertSame($factory, $this->inputFilter->getFactory()); } - public function testCanAddUsingSpecification() + public function inputProvider() { - $this->filter->add(array( - 'name' => 'foo', - )); - $this->assertTrue($this->filter->has('foo')); - $foo = $this->filter->get('foo'); - $this->assertInstanceOf('Zend\InputFilter\InputInterface', $foo); - } + $dataSets = parent::inputProvider(); - /** - * @covers \Zend\InputFilter\BaseInputFilter::getValue - * - * @group 6028 - */ - public function testGetValueReturnsArrayIfNestedInputFilters() - { - $inputFilter = new InputFilter(); - $inputFilter->add(new Input(), 'name'); + $inputSpecificationAsArray = array( + 'name' => 'inputFoo', + ); + $inputSpecificationAsTraversable = new ArrayIterator($inputSpecificationAsArray); - $this->filter->add($inputFilter, 'people'); + $inputSpecificationResult = new Input('inputFoo'); + $inputSpecificationResult->getFilterChain(); // Fill input with a default chain just for make the test pass + $inputSpecificationResult->getValidatorChain(); // Fill input with a default chain just for make the test pass - $data = array( - 'people' => array( - 'name' => 'Wanderson' - ) + // @codingStandardsIgnoreStart + $inputFilterDataSets = array( + // Description => [input, expected name, $expectedReturnInput] + 'array' => array($inputSpecificationAsArray , 'inputFoo', $inputSpecificationResult), + 'Traversable' => array($inputSpecificationAsTraversable , 'inputFoo', $inputSpecificationResult), ); + // @codingStandardsIgnoreEnd + $dataSets = array_merge($dataSets, $inputFilterDataSets); - $this->filter->setData($data); - $this->assertTrue($this->filter->isValid()); - - $this->assertInternalType('array', $this->filter->getValue('people')); + return $dataSets; } /** - * @group ZF2-5648 + * @return Factory|MockObject */ - public function testCountZeroValidateInternalInputWithCollectionInputFilter() - { - $inputFilter = new InputFilter(); - $inputFilter->add(new Input(), 'name'); - - $collection = new CollectionInputFilter(); - $collection->setInputFilter($inputFilter); - $collection->setCount(0); - - $this->filter->add($collection, 'people'); - - $data = array( - 'people' => array( - array( - 'name' => 'Wanderson', - ), - ), - ); - $this->filter->setData($data); - - $this->assertTrue($this->filter->isvalid()); - $this->assertSame($data, $this->filter->getValues()); - } - - public function testCanUseContextPassedToInputFilter() + protected function createFactoryMock() { - $context = new \stdClass(); - - $input = $this->getMock('Zend\InputFilter\InputInterface'); - $input->expects($this->once())->method('isValid')->with($context)->will($this->returnValue(true)); - $input->expects($this->any())->method('getRawValue')->will($this->returnValue('Mwop')); - - $this->filter->add($input, 'username'); - $this->filter->setData(array('username' => 'Mwop')); + /** @var Factory|MockObject $factory */ + $factory = $this->getMock('Zend\InputFilter\Factory'); - $this->filter->isValid($context); + return $factory; } } diff --git a/test/InputTest.php b/test/InputTest.php index 9203912c..f21134c9 100644 --- a/test/InputTest.php +++ b/test/InputTest.php @@ -9,12 +9,19 @@ namespace ZendTest\InputFilter; +use PHPUnit_Framework_MockObject_MockObject as MockObject; use PHPUnit_Framework_TestCase as TestCase; -use RuntimeException; -use Zend\Filter; +use stdClass; +use Zend\Filter\FilterChain; use Zend\InputFilter\Input; -use Zend\Validator; +use Zend\InputFilter\InputInterface; +use Zend\Validator\NotEmpty as NotEmptyValidator; +use Zend\Validator\ValidatorChain; +use Zend\Validator\ValidatorInterface; +/** + * @covers Zend\InputFilter\Input + */ class InputTest extends TestCase { /** @@ -27,6 +34,21 @@ public function setUp() $this->input = new Input('foo'); } + public function assertRequiredValidationErrorMessage(Input $input, $message = '') + { + $message = $message ?: 'Expected failure message for required input'; + $message .= ';'; + + $messages = $input->getMessages(); + $this->assertInternalType('array', $messages, $message . ' non-array messages array'); + + $notEmpty = new NotEmptyValidator(); + $messageTemplates = $notEmpty->getOption('messageTemplates'); + $this->assertSame(array( + NotEmptyValidator::IS_EMPTY => $messageTemplates[NotEmptyValidator::IS_EMPTY], + ), $messages, $message . ' missing NotEmpty::IS_EMPTY key and/or contains additional messages'); + } + public function testConstructorRequiresAName() { $this->assertEquals('foo', $this->input->getName()); @@ -48,14 +70,14 @@ public function testInputHasEmptyValidatorChainByDefault() public function testCanInjectFilterChain() { - $chain = new Filter\FilterChain(); + $chain = $this->createFilterChainMock(); $this->input->setFilterChain($chain); $this->assertSame($chain, $this->input->getFilterChain()); } public function testCanInjectValidatorChain() { - $chain = new Validator\ValidatorChain(); + $chain = $this->createValidatorChainMock(); $this->input->setValidatorChain($chain); $this->assertSame($chain, $this->input->getValidatorChain()); } @@ -84,101 +106,227 @@ public function testAllowEmptyFlagIsMutable() public function testContinueIfEmptyFlagIsFalseByDefault() { - $input = new Input('foo'); + $input = $this->input; $this->assertFalse($input->continueIfEmpty()); } public function testContinueIfEmptyFlagIsMutable() { - $input = new Input('foo'); + $input = $this->input; $input->setContinueIfEmpty(true); $this->assertTrue($input->continueIfEmpty()); } - public function testNotEmptyValidatorNotInjectedIfContinueIfEmptyIsTrue() + /** + * @dataProvider setValueProvider + */ + public function testSetFallbackValue($fallbackValue) + { + $input = $this->input; + + $return = $input->setFallbackValue($fallbackValue); + $this->assertSame($input, $return, 'setFallbackValue() must return it self'); + + $this->assertEquals($fallbackValue, $input->getFallbackValue(), 'getFallbackValue() value not match'); + $this->assertEquals(true, $input->hasFallback(), 'hasFallback() value not match'); + } + + /** + * @dataProvider fallbackValueVsIsValidProvider + */ + public function testFallbackValueVsIsValidRules($required, $fallbackValue, $originalValue, $isValid, $expectedValue) + { + $input = $this->input; + $input->setContinueIfEmpty(true); + + $input->setRequired($required); + $input->setValidatorChain($this->createValidatorChainMock($isValid, $originalValue)); + $input->setFallbackValue($fallbackValue); + $input->setValue($originalValue); + + $this->assertTrue( + $input->isValid(), + 'isValid() should be return always true when fallback value is set. Detail: ' . + json_encode($input->getMessages()) + ); + $this->assertEquals(array(), $input->getMessages(), 'getMessages() should be empty because the input is valid'); + $this->assertSame($expectedValue, $input->getRawValue(), 'getRawValue() value not match'); + $this->assertSame($expectedValue, $input->getValue(), 'getValue() value not match'); + } + + /** + * @dataProvider fallbackValueVsIsValidProvider + */ + public function testFallbackValueVsIsValidRulesWhenValueNotSet($required, $fallbackValue) { - $input = new Input('foo'); + $expectedValue = $fallbackValue; // Should always return the fallback value + + $input = $this->input; $input->setContinueIfEmpty(true); - $input->setValue(''); + + $input->setRequired($required); + $input->setValidatorChain($this->createValidatorChainMock(null)); + $input->setFallbackValue($fallbackValue); + + $this->assertTrue( + $input->isValid(), + 'isValid() should be return always true when fallback value is set. Detail: ' . + json_encode($input->getMessages()) + ); + $this->assertEquals(array(), $input->getMessages(), 'getMessages() should be empty because the input is valid'); + $this->assertSame($expectedValue, $input->getRawValue(), 'getRawValue() value not match'); + $this->assertSame($expectedValue, $input->getValue(), 'getValue() value not match'); + } + + public function testRequiredWithoutFallbackAndValueNotSetThenFail() + { + $input = $this->input; + $input->setRequired(true); + + $this->assertFalse( + $input->isValid(), + 'isValid() should be return always false when no fallback value, is required, and not data is set.' + ); + $this->assertRequiredValidationErrorMessage($input); + } + + public function testRequiredWithoutFallbackAndValueNotSetThenFailReturnsCustomErrorMessageWhenSet() + { + $input = $this->input; + $input->setRequired(true); + $input->setErrorMessage('FAILED TO VALIDATE'); + + $this->assertFalse( + $input->isValid(), + 'isValid() should be return always false when no fallback value, is required, and not data is set.' + ); + $this->assertSame(array('FAILED TO VALIDATE'), $input->getMessages()); + } + + /** + * @group 28 + * @group 60 + */ + public function testRequiredWithoutFallbackAndValueNotSetProvidesNotEmptyValidatorIsEmptyErrorMessage() + { + $input = $this->input; + $input->setRequired(true); + + $this->assertFalse( + $input->isValid(), + 'isValid() should always return false when no fallback value is present, ' + . 'the input is required, and no data is set.' + ); + $this->assertRequiredValidationErrorMessage($input); + } + + /** + * @group 28 + * @group 60 + */ + public function testRequiredWithoutFallbackAndValueNotSetProvidesCustomErrorMessageWhenSet() + { + $input = $this->input; + $input->setRequired(true); + $input->setErrorMessage('FAILED TO VALIDATE'); + + $this->assertFalse( + $input->isValid(), + 'isValid() should always return false when no fallback value is present, ' + . 'the input is required, and no data is set.' + ); + $this->assertSame(array('FAILED TO VALIDATE'), $input->getMessages()); + } + + public function testNotRequiredWithoutFallbackAndValueNotSetThenIsValid() + { + $input = $this->input; + $input->setRequired(false); + $input->setAllowEmpty(false); + $input->setContinueIfEmpty(true); + + // Validator should not to be called + $input->getValidatorChain() + ->attach($this->createValidatorMock(null, null)) + ; + $this->assertTrue( + $input->isValid(), + 'isValid() should be return always true when is not required, and no data is set. Detail: ' . + json_encode($input->getMessages()) + ); + $this->assertEquals(array(), $input->getMessages(), 'getMessages() should be empty because the input is valid'); + } + + /** + * @dataProvider emptyValueProvider + */ + public function testNotEmptyValidatorNotInjectedIfContinueIfEmptyIsTrue($value) + { + $input = $this->input; + $input->setContinueIfEmpty(true); + $input->setValue($value); $input->isValid(); $validators = $input->getValidatorChain() ->getValidators(); $this->assertEmpty($validators); } - public function testValueIsNullByDefault() + public function testDefaultGetValue() { $this->assertNull($this->input->getValue()); } public function testValueMayBeInjected() { - $this->input->setValue('bar'); - $this->assertEquals('bar', $this->input->getValue()); + $valueRaw = $this->getDummyValue(); + + $this->input->setValue($valueRaw); + $this->assertEquals($valueRaw, $this->input->getValue()); } public function testRetrievingValueFiltersTheValue() { - $this->input->setValue('bar'); - $filter = new Filter\StringToUpper(); - $this->input->getFilterChain()->attach($filter); - $this->assertEquals('BAR', $this->input->getValue()); + $valueRaw = $this->getDummyValue(); + $valueFiltered = $this->getDummyValue(false); + + $filterChain = $this->createFilterChainMock($valueRaw, $valueFiltered); + + $this->input->setFilterChain($filterChain); + $this->input->setValue($valueRaw); + + $this->assertSame($valueFiltered, $this->input->getValue()); } public function testCanRetrieveRawValue() { - $this->input->setValue('bar'); - $filter = new Filter\StringToUpper(); - $this->input->getFilterChain()->attach($filter); - $this->assertEquals('bar', $this->input->getRawValue()); - } + $valueRaw = $this->getDummyValue(); - public function testIsValidReturnsFalseIfValidationChainFails() - { - $this->input->setValue('bar'); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - } + $filterChain = $this->createFilterChainMock(); - public function testIsValidReturnsTrueIfValidationChainSucceeds() - { - $this->input->setValue('123'); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); + $this->input->setFilterChain($filterChain); + $this->input->setValue($valueRaw); + + $this->assertEquals($valueRaw, $this->input->getRawValue()); } public function testValidationOperatesOnFilteredValue() { - $this->input->setValue(' 123 '); - $filter = new Filter\StringTrim(); - $this->input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertTrue($this->input->isValid()); - } + $valueRaw = $this->getDummyValue(); + $valueFiltered = $this->getDummyValue(false); - public function testGetMessagesReturnsValidationMessages() - { - $this->input->setValue('bar'); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayHasKey(Validator\Digits::NOT_DIGITS, $messages); - } + $filterChain = $this->createFilterChainMock($valueRaw, $valueFiltered); - public function testSpecifyingMessagesToInputReturnsThoseOnFailedValidation() - { - $this->input->setValue('bar'); - $validator = new Validator\Digits(); - $this->input->getValidatorChain()->attach($validator); - $this->input->setErrorMessage('Please enter only digits'); - $this->assertFalse($this->input->isValid()); - $messages = $this->input->getMessages(); - $this->assertArrayNotHasKey(Validator\Digits::NOT_DIGITS, $messages); - $this->assertContains('Please enter only digits', $messages); + $validatorChain = $this->createValidatorChainMock(true, $valueFiltered); + + $this->input->setAllowEmpty(true); + $this->input->setFilterChain($filterChain); + $this->input->setValidatorChain($validatorChain); + $this->input->setValue($valueRaw); + + $this->assertTrue( + $this->input->isValid(), + 'isValid() value not match. Detail . ' . json_encode($this->input->getMessages()) + ); } public function testBreakOnFailureFlagIsOffByDefault() @@ -192,10 +340,13 @@ public function testBreakOnFailureFlagIsMutable() $this->assertTrue($this->input->breakOnFailure()); } - public function testNotEmptyValidatorAddedWhenIsValidIsCalled() + /** + * @dataProvider emptyValueProvider + */ + public function testNotEmptyValidatorAddedWhenIsValidIsCalled($value) { $this->assertTrue($this->input->isRequired()); - $this->input->setValue(''); + $this->input->setValue($value); $validatorChain = $this->input->getValidatorChain(); $this->assertEquals(0, count($validatorChain->getValidators())); @@ -209,15 +360,15 @@ public function testNotEmptyValidatorAddedWhenIsValidIsCalled() $this->assertEquals(1, count($validatorChain->getValidators())); } - public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() + /** + * @dataProvider emptyValueProvider + */ + public function testRequiredNotEmptyValidatorNotAddedWhenOneExists($value) { - $this->assertTrue($this->input->isRequired()); - $this->input->setValue(''); + $this->input->setRequired(true); + $this->input->setValue($value); - $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); - $notEmptyMock->expects($this->exactly(1)) - ->method('isValid') - ->will($this->returnValue(false)); + $notEmptyMock = $this->createNonEmptyValidatorMock(false, $value); $validatorChain = $this->input->getValidatorChain(); $validatorChain->prependValidator($notEmptyMock); @@ -228,127 +379,23 @@ public function testRequiredNotEmptyValidatorNotAddedWhenOneExists() $this->assertEquals($notEmptyMock, $validators[0]['instance']); } - public function emptyValuesProvider() - { - return array( - array(null), - array(''), - array(array()), - ); - } - - /** - * @dataProvider emptyValuesProvider - */ - public function testValidatorSkippedIfValueIsEmptyAndAllowedAndNotContinue($emptyValue) - { - $validator = function () { - return false; - }; - $this->input->setAllowEmpty(true) - ->setContinueIfEmpty(false) - ->setValue($emptyValue) - ->getValidatorChain()->attach(new Validator\Callback($validator)); - - $this->assertTrue($this->input->isValid()); - } - - /** - * @dataProvider emptyValuesProvider - */ - public function testAllowEmptyOptionSet($emptyValue) - { - $this->input->setAllowEmpty(true); - $this->input->setValue($emptyValue); - $this->assertTrue($this->input->isValid()); - } - /** - * @dataProvider emptyValuesProvider + * @dataProvider emptyValueProvider */ - public function testAllowEmptyOptionNotSet($emptyValue) + public function testDoNotInjectNotEmptyValidatorIfAnywhereInChain($valueRaw, $valueFiltered) { - $this->input->setAllowEmpty(false); - $this->input->setValue($emptyValue); - $this->assertFalse($this->input->isValid()); - } - - /** - * @dataProvider emptyValuesProvider - */ - public function testValidatorInvokedIfValueIsEmptyAndAllowedAndContinue($emptyValue) - { - $message = 'failure by explicit validator'; - $validator = new Validator\Callback(function ($value) { - return false; - }); - $validator->setMessage($message); - $this->input->setAllowEmpty(true) - ->setContinueIfEmpty(true) - ->setValue($emptyValue) - ->getValidatorChain()->attach($validator); - $this->assertFalse($this->input->isValid()); - // Test reason for validation failure; ensures that failure was not - // caused by accidentally injected NotEmpty validator - $this->assertEquals(array('callbackValue' => $message), $this->input->getMessages()); - } - - public function testNotAllowEmptyWithFilterConvertsNonemptyToEmptyIsNotValid() - { - $this->input->setValue('nonempty') - ->getFilterChain()->attach(new Filter\Callback(function () { - return ''; - })); - $this->assertFalse($this->input->isValid()); - } - - public function testNotAllowEmptyWithFilterConvertsEmptyToNonEmptyIsValid() - { - $this->input->setValue('') - ->getFilterChain()->attach(new Filter\Callback(function () { - return 'nonempty'; - })); - $this->assertTrue($this->input->isValid()); - } - - public function testMerge() - { - $input = new Input('foo'); - $input->setValue(' 123 '); - $filter = new Filter\StringTrim(); - $input->getFilterChain()->attach($filter); - $validator = new Validator\Digits(); - $input->getValidatorChain()->attach($validator); - - $input2 = new Input('bar'); - $input2->merge($input); - $validatorChain = $input->getValidatorChain(); - $filterChain = $input->getFilterChain(); - - $this->assertEquals(' 123 ', $input2->getRawValue()); - $this->assertEquals(1, $validatorChain->count()); - $this->assertEquals(1, $filterChain->count()); - - $validators = $validatorChain->getValidators(); - $this->assertInstanceOf('Zend\Validator\Digits', $validators[0]['instance']); + $filterChain = $this->createFilterChainMock($valueRaw, $valueFiltered); + $validatorChain = $this->input->getValidatorChain(); - $filters = $filterChain->getFilters()->toArray(); - $this->assertInstanceOf('Zend\Filter\StringTrim', $filters[0]); - } + $this->input->setRequired(true); + $this->input->setFilterChain($filterChain); + $this->input->setValue($valueRaw); - public function testDoNotInjectNotEmptyValidatorIfAnywhereInChain() - { - $this->assertTrue($this->input->isRequired()); - $this->input->setValue(''); + $notEmptyMock = $this->createNonEmptyValidatorMock(false, $valueFiltered); - $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); - $notEmptyMock->expects($this->exactly(1)) - ->method('isValid') - ->will($this->returnValue(false)); - - $validatorChain = $this->input->getValidatorChain(); - $validatorChain->attach(new Validator\Digits()); + $validatorChain->attach($this->createValidatorMock(true)); $validatorChain->attach($notEmptyMock); + $this->assertFalse($this->input->isValid()); $validators = $validatorChain->getValidators(); @@ -356,502 +403,480 @@ public function testDoNotInjectNotEmptyValidatorIfAnywhereInChain() $this->assertEquals($notEmptyMock, $validators[1]['instance']); } - public function dataFallbackValue() - { - return array( - array( - 'fallbackValue' => null - ), - array( - 'fallbackValue' => '' - ), - array( - 'fallbackValue' => 'some value' - ), + /** + * @group 7448 + * @dataProvider isRequiredVsAllowEmptyVsContinueIfEmptyVsIsValidProvider + */ + public function testIsRequiredVsAllowEmptyVsContinueIfEmptyVsIsValid( + $required, + $allowEmpty, + $continueIfEmpty, + $validator, + $value, + $expectedIsValid, + $expectedMessages + ) { + $this->input->setRequired($required); + $this->input->setAllowEmpty($allowEmpty); + $this->input->setContinueIfEmpty($continueIfEmpty); + $this->input->getValidatorChain() + ->attach($validator) + ; + $this->input->setValue($value); + + $this->assertEquals( + $expectedIsValid, + $this->input->isValid(), + 'isValid() value not match. Detail: ' . json_encode($this->input->getMessages()) ); + $this->assertEquals($expectedMessages, $this->input->getMessages(), 'getMessages() value not match'); + $this->assertEquals($value, $this->input->getRawValue(), 'getRawValue() must return the value always'); + $this->assertEquals($value, $this->input->getValue(), 'getValue() must return the filtered value always'); } /** - * @dataProvider dataFallbackValue + * @dataProvider setValueProvider */ - public function testFallbackValue($fallbackValue) + public function testSetValuePutInputInTheDesiredState($value) { - $this->input->setFallbackValue($fallbackValue); - $validator = new Validator\Date(); - $this->input->getValidatorChain()->attach($validator); - $this->input->setValue('123'); // not a date + $input = $this->input; + $this->assertFalse($input->hasValue(), 'Input should not have value by default'); - $this->assertTrue($this->input->isValid()); - $this->assertEmpty($this->input->getMessages()); - $this->assertSame($fallbackValue, $this->input->getValue()); + $input->setValue($value); + $this->assertTrue($input->hasValue(), "hasValue() didn't return true when value was set"); } - public function testMergeRetainsContinueIfEmptyFlag() + /** + * @dataProvider setValueProvider + */ + public function testResetValueReturnsInputValueToDefaultValue($value) { - $input = new Input('foo'); - $input->setContinueIfEmpty(true); + $input = $this->input; + $originalInput = clone $input; + $this->assertFalse($input->hasValue(), 'Input should not have value by default'); + + $input->setValue($value); + $this->assertTrue($input->hasValue(), "hasValue() didn't return true when value was set"); - $input2 = new Input('bar'); - $input2->merge($input); - $this->assertTrue($input2->continueIfEmpty()); + $return = $input->resetValue(); + $this->assertSame($input, $return, 'resetValue() must return itself'); + $this->assertEquals($originalInput, $input, 'Input was not reset to the default value state'); } - public function testMergeRetainsAllowEmptyFlag() + public function testMerge() { - $input = new Input('foo'); - $input->setRequired(true); - $input->setAllowEmpty(true); - - $input2 = new Input('bar'); - $input2->setRequired(true); - $input2->setAllowEmpty(false); - $input2->merge($input); - - $this->assertTrue($input2->isRequired()); - $this->assertTrue($input2->allowEmpty()); + $sourceRawValue = $this->getDummyValue(); + + $source = $this->createInputInterfaceMock(); + $source->method('getName')->willReturn('bazInput'); + $source->method('getErrorMessage')->willReturn('bazErrorMessage'); + $source->method('breakOnFailure')->willReturn(true); + $source->method('isRequired')->willReturn(true); + $source->method('getRawValue')->willReturn($sourceRawValue); + $source->method('getFilterChain')->willReturn($this->createFilterChainMock()); + $source->method('getValidatorChain')->willReturn($this->createValidatorChainMock()); + + $targetFilterChain = $this->createFilterChainMock(); + $targetFilterChain->expects(TestCase::once()) + ->method('merge') + ->with($source->getFilterChain()) + ; + + $targetValidatorChain = $this->createValidatorChainMock(); + $targetValidatorChain->expects(TestCase::once()) + ->method('merge') + ->with($source->getValidatorChain()) + ; + + $target = $this->input; + $target->setName('fooInput'); + $target->setErrorMessage('fooErrorMessage'); + $target->setBreakOnFailure(false); + $target->setRequired(false); + $target->setFilterChain($targetFilterChain); + $target->setValidatorChain($targetValidatorChain); + + $return = $target->merge($source); + $this->assertSame($target, $return, 'merge() must return it self'); + + $this->assertEquals('bazInput', $target->getName(), 'getName() value not match'); + $this->assertEquals('bazErrorMessage', $target->getErrorMessage(), 'getErrorMessage() value not match'); + $this->assertEquals(true, $target->breakOnFailure(), 'breakOnFailure() value not match'); + $this->assertEquals(true, $target->isRequired(), 'isRequired() value not match'); + $this->assertEquals($sourceRawValue, $target->getRawValue(), 'getRawValue() value not match'); + $this->assertTrue($target->hasValue(), 'hasValue() value not match'); } /** - * @group 7445 + * Specific Input::merge extras */ - public function testInputIsValidWhenUsingSetRequiredAtStart() + public function testInputMergeWithoutValues() { - $input = new Input(); - $input->setName('foo') - ->setRequired(false) - ->setAllowEmpty(false) - ->setContinueIfEmpty(false); + $source = new Input(); + $source->setContinueIfEmpty(true); + $this->assertFalse($source->hasValue(), 'Source should not have a value'); - $this->assertTrue($input->isValid()); - } + $target = $this->input; + $target->setContinueIfEmpty(false); + $this->assertFalse($target->hasValue(), 'Target should not have a value'); - /** - * @group 7445 - */ - public function testInputIsValidWhenUsingSetRequiredAtEnd() - { - $input = new Input(); - $input->setName('foo') - ->setAllowEmpty(false) - ->setContinueIfEmpty(false) - ->setRequired(false); + $return = $target->merge($source); + $this->assertSame($target, $return, 'merge() must return it self'); - $this->assertTrue($input->isValid()); + $this->assertEquals(true, $target->continueIfEmpty(), 'continueIfEmpty() value not match'); + $this->assertFalse($target->hasValue(), 'hasValue() value not match'); } - public function whenRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun() + /** + * Specific Input::merge extras + */ + public function testInputMergeWithSourceValue() { - $validator = new Validator\Callback(function ($value) { - throw new RuntimeException('Validator executed when it should not be'); - }); + $source = new Input(); + $source->setContinueIfEmpty(true); + $source->setValue(array('foo')); - $requiredFirst = new Input('foo'); - $requiredFirst->setRequired(true) - ->setAllowEmpty(true) - ->setContinueIfEmpty(false) - ->getValidatorChain()->attach($validator); + $target = $this->input; + $target->setContinueIfEmpty(false); + $this->assertFalse($target->hasValue(), 'Target should not have a value'); - $requiredLast = new Input('foo'); - $requiredLast->setAllowEmpty(true) - ->setContinueIfEmpty(false) - ->setRequired(true) - ->getValidatorChain()->attach($validator); + $return = $target->merge($source); + $this->assertSame($target, $return, 'merge() must return it self'); - return array( - 'required-first-null' => array($requiredFirst, null), - 'required-last-null' => array($requiredLast, null), - 'required-first-empty' => array($requiredFirst, ''), - 'required-last-empty' => array($requiredLast, ''), - 'required-first-array' => array($requiredFirst, array()), - 'required-last-array' => array($requiredLast, array()), - ); + $this->assertEquals(true, $target->continueIfEmpty(), 'continueIfEmpty() value not match'); + $this->assertEquals(array('foo'), $target->getRawValue(), 'getRawValue() value not match'); + $this->assertTrue($target->hasValue(), 'hasValue() value not match'); } /** - * @group 7448 - * @dataProvider whenRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun + * Specific Input::merge extras */ - public function testWhenRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun($input, $value) + public function testInputMergeWithTargetValue() { - $input->setValue($value); - $this->assertTrue($input->isValid()); - } + $source = new Input(); + $source->setContinueIfEmpty(true); + $this->assertFalse($source->hasValue(), 'Source should not have a value'); - public function whenRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun() - { - $alwaysInvalid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return false; - }); + $target = $this->input; + $target->setContinueIfEmpty(false); + $target->setValue(array('foo')); - $emptyIsValid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return true; - }); - - $requiredFirstInvalid = new Input('foo'); - $requiredFirstValid = new Input('foo'); - foreach (array($requiredFirstValid, $requiredFirstInvalid) as $input) { - $input->setRequired(true) - ->setAllowEmpty(true) - ->setContinueIfEmpty(true); - } + $return = $target->merge($source); + $this->assertSame($target, $return, 'merge() must return it self'); - $requiredLastInvalid = new Input('foo'); - $requiredLastValid = new Input('foo'); - foreach (array($requiredLastValid, $requiredLastInvalid) as $input) { - $input->setAllowEmpty(true) - ->setContinueIfEmpty(true) - ->setRequired(true); - } + $this->assertEquals(true, $target->continueIfEmpty(), 'continueIfEmpty() value not match'); + $this->assertEquals(array('foo'), $target->getRawValue(), 'getRawValue() value not match'); + $this->assertTrue($target->hasValue(), 'hasValue() value not match'); + } - foreach (array($requiredFirstValid, $requiredLastValid) as $input) { - $input->getValidatorChain()->attach($emptyIsValid); - } + public function fallbackValueVsIsValidProvider() + { + $required = true; + $isValid = true; - foreach (array($requiredFirstInvalid, $requiredLastInvalid) as $input) { - $input->getValidatorChain()->attach($alwaysInvalid); - } + $originalValue = 'fooValue'; + $fallbackValue = 'fooFallbackValue'; + // @codingStandardsIgnoreStart return array( - 'required-first-null-valid' => array($requiredFirstValid, null, 'assertTrue'), - 'required-first-null-invalid' => array($requiredFirstInvalid, null, 'assertFalse'), - 'required-last-null-valid' => array($requiredLastValid, null, 'assertTrue'), - 'required-last-null-invalid' => array($requiredLastInvalid, null, 'assertFalse'), - 'required-first-empty-valid' => array($requiredFirstValid, '', 'assertTrue'), - 'required-first-empty-invalid' => array($requiredFirstInvalid, '', 'assertFalse'), - 'required-last-empty-valid' => array($requiredLastValid, '', 'assertTrue'), - 'required-last-empty-invalid' => array($requiredLastInvalid, '', 'assertFalse'), - 'required-first-array-valid' => array($requiredFirstValid, array(), 'assertTrue'), - 'required-first-array-invalid' => array($requiredFirstInvalid, array(), 'assertFalse'), - 'required-last-array-valid' => array($requiredLastValid, array(), 'assertTrue'), - 'required-last-array-invalid' => array($requiredLastInvalid, array(), 'assertFalse'), + // Description => [$inputIsRequired, $fallbackValue, $originalValue, $isValid, $expectedValue] + 'Required: T, Input: Invalid. getValue: fallback' => array( $required, $fallbackValue, $originalValue, !$isValid, $fallbackValue), + 'Required: T, Input: Valid. getValue: original' => array( $required, $fallbackValue, $originalValue, $isValid, $originalValue), + 'Required: F, Input: Invalid. getValue: fallback' => array(!$required, $fallbackValue, $originalValue, !$isValid, $fallbackValue), + 'Required: F, Input: Valid. getValue: original' => array(!$required, $fallbackValue, $originalValue, $isValid, $originalValue), ); + // @codingStandardsIgnoreEnd } - /** - * @group 7448 - * @dataProvider whenRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun - */ - public function testWhenRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun($input, $value, $assertion) - { - $input->setValue($value); - $this->{$assertion}($input->isValid()); - } - - public function whenRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun() + public function setValueProvider() { - $validator = new Validator\Callback(function ($value) { - throw new RuntimeException('Validator executed when it should not be'); - }); + $emptyValues = $this->emptyValueProvider(); + $mixedValues = $this->mixedValueProvider(); - $requiredFirst = new Input('foo'); - $requiredFirst->setRequired(true) - ->setAllowEmpty(false) - ->setContinueIfEmpty(false) - ->getValidatorChain()->attach($validator); + $values = array_merge($emptyValues, $mixedValues); - $requiredLast = new Input('foo'); - $requiredLast->setAllowEmpty(false) - ->setContinueIfEmpty(false) - ->setRequired(true) - ->getValidatorChain()->attach($validator); - - return array( - 'required-first-null' => array($requiredFirst, null), - 'required-last-null' => array($requiredLast, null), - 'required-first-empty' => array($requiredFirst, ''), - 'required-last-empty' => array($requiredLast, ''), - 'required-first-array' => array($requiredFirst, array()), - 'required-last-array' => array($requiredLast, array()), - ); + return $values; } - /** - * @group 7448 - * @dataProvider whenRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun - */ - public function testWhenRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun($input, $value) + public function isRequiredVsAllowEmptyVsContinueIfEmptyVsIsValidProvider() { - $input->setValue($value); - $this->assertFalse($input->isValid()); - } + $allValues = $this->setValueProvider(); + $emptyValues = $this->emptyValueProvider(); + $nonEmptyValues = array_diff_key($allValues, $emptyValues); - public function whenRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun() - { - $alwaysInvalid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return false; - }); + $isRequired = true; + $aEmpty = true; + $cIEmpty = true; + $isValid = true; - $emptyIsValid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return true; - }); - - $requiredFirstInvalid = new Input('foo'); - $requiredFirstValid = new Input('foo'); - foreach (array($requiredFirstValid, $requiredFirstInvalid) as $input) { - $input->setRequired(true) - ->setAllowEmpty(false) - ->setContinueIfEmpty(true); - } + $validatorMsg = array('FooValidator' => 'Invalid Value'); + $notEmptyMsg = array('isEmpty' => "Value is required and can't be empty"); - $requiredLastInvalid = new Input('foo'); - $requiredLastValid = new Input('foo'); - foreach (array($requiredLastValid, $requiredLastInvalid) as $input) { - $input->setAllowEmpty(false) - ->setContinueIfEmpty(true) - ->setRequired(true); - } + $self = $this; + $validatorNotCall = function ($value, $context = null) use ($self) { + return $self->createValidatorMock(null, $value, $context); + }; + $validatorInvalid = function ($value, $context = null) use ($self, $validatorMsg) { + return $self->createValidatorMock(false, $value, $context, $validatorMsg); + }; + $validatorValid = function ($value, $context = null) use ($self) { + return $self->createValidatorMock(true, $value, $context); + }; - foreach (array($requiredFirstValid, $requiredLastValid) as $input) { - $input->getValidatorChain()->attach($emptyIsValid); - } + // @codingStandardsIgnoreStart + $dataTemplates = array( + // Description => [$isRequired, $allowEmpty, $continueIfEmpty, $validator, [$values], $expectedIsValid, $expectedMessages] + 'Required: T; AEmpty: T; CIEmpty: T; Validator: T' => array( $isRequired, $aEmpty, $cIEmpty, $validatorValid , $allValues , $isValid, array()), + 'Required: T; AEmpty: T; CIEmpty: T; Validator: F' => array( $isRequired, $aEmpty, $cIEmpty, $validatorInvalid, $allValues , !$isValid, $validatorMsg), - foreach (array($requiredFirstInvalid, $requiredLastInvalid) as $input) { - $input->getValidatorChain()->attach($alwaysInvalid); - } + 'Required: T; AEmpty: T; CIEmpty: F; Validator: X, Value: Empty' => array( $isRequired, $aEmpty, !$cIEmpty, $validatorNotCall, $emptyValues , $isValid, array()), + 'Required: T; AEmpty: T; CIEmpty: F; Validator: T, Value: Not Empty' => array( $isRequired, $aEmpty, !$cIEmpty, $validatorValid , $nonEmptyValues, $isValid, array()), + 'Required: T; AEmpty: T; CIEmpty: F; Validator: F, Value: Not Empty' => array( $isRequired, $aEmpty, !$cIEmpty, $validatorInvalid, $nonEmptyValues, !$isValid, $validatorMsg), - return array( - 'required-first-null-valid' => array($requiredFirstValid, null, 'assertTrue'), - 'required-first-null-invalid' => array($requiredFirstInvalid, null, 'assertFalse'), - 'required-last-null-valid' => array($requiredLastValid, null, 'assertTrue'), - 'required-last-null-invalid' => array($requiredLastInvalid, null, 'assertFalse'), - 'required-first-empty-valid' => array($requiredFirstValid, '', 'assertTrue'), - 'required-first-empty-invalid' => array($requiredFirstInvalid, '', 'assertFalse'), - 'required-last-empty-valid' => array($requiredLastValid, '', 'assertTrue'), - 'required-last-empty-invalid' => array($requiredLastInvalid, '', 'assertFalse'), - 'required-first-array-valid' => array($requiredFirstValid, array(), 'assertTrue'), - 'required-first-array-invalid' => array($requiredFirstInvalid, array(), 'assertFalse'), - 'required-last-array-valid' => array($requiredLastValid, array(), 'assertTrue'), - 'required-last-array-invalid' => array($requiredLastInvalid, array(), 'assertFalse'), - ); - } + 'Required: T; AEmpty: F; CIEmpty: T; Validator: T' => array( $isRequired, !$aEmpty, $cIEmpty, $validatorValid , $allValues , $isValid, array()), + 'Required: T; AEmpty: F; CIEmpty: T; Validator: F' => array( $isRequired, !$aEmpty, $cIEmpty, $validatorInvalid, $allValues , !$isValid, $validatorMsg), - /** - * @group 7448 - * @dataProvider whenRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun - */ - public function testWhenRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun($input, $value, $assertion) - { - $input->setValue($value); - $this->{$assertion}($input->isValid()); - } + 'Required: T; AEmpty: F; CIEmpty: F; Validator: X, Value: Empty' => array( $isRequired, !$aEmpty, !$cIEmpty, $validatorNotCall, $emptyValues , !$isValid, $notEmptyMsg), + 'Required: T; AEmpty: F; CIEmpty: F; Validator: T, Value: Not Empty' => array( $isRequired, !$aEmpty, !$cIEmpty, $validatorValid , $nonEmptyValues, $isValid, array()), + 'Required: T; AEmpty: F; CIEmpty: F; Validator: F, Value: Not Empty' => array( $isRequired, !$aEmpty, !$cIEmpty, $validatorInvalid, $nonEmptyValues, !$isValid, $validatorMsg), - public function whenNotRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun() - { - $validator = new Validator\Callback(function ($value) { - throw new RuntimeException('Validator executed when it should not be'); - }); + 'Required: F; AEmpty: T; CIEmpty: T; Validator: T' => array(!$isRequired, $aEmpty, $cIEmpty, $validatorValid , $allValues , $isValid, array()), + 'Required: F; AEmpty: T; CIEmpty: T; Validator: F' => array(!$isRequired, $aEmpty, $cIEmpty, $validatorInvalid, $allValues , !$isValid, $validatorMsg), - $requiredFirst = new Input('foo'); - $requiredFirst->setRequired(false) - ->setAllowEmpty(true) - ->setContinueIfEmpty(false) - ->getValidatorChain()->attach($validator); + 'Required: F; AEmpty: T; CIEmpty: F; Validator: X, Value: Empty' => array(!$isRequired, $aEmpty, !$cIEmpty, $validatorNotCall, $emptyValues , $isValid, array()), + 'Required: F; AEmpty: T; CIEmpty: F; Validator: T, Value: Not Empty' => array(!$isRequired, $aEmpty, !$cIEmpty, $validatorValid , $nonEmptyValues, $isValid, array()), + 'Required: F; AEmpty: T; CIEmpty: F; Validator: F, Value: Not Empty' => array(!$isRequired, $aEmpty, !$cIEmpty, $validatorInvalid, $nonEmptyValues, !$isValid, $validatorMsg), - $requiredLast = new Input('foo'); - $requiredLast->setAllowEmpty(true) - ->setContinueIfEmpty(false) - ->setRequired(false) - ->getValidatorChain()->attach($validator); + 'Required: F; AEmpty: F; CIEmpty: T; Validator: T' => array(!$isRequired, !$aEmpty, $cIEmpty, $validatorValid , $allValues , $isValid, array()), + 'Required: F; AEmpty: F; CIEmpty: T; Validator: F' => array(!$isRequired, !$aEmpty, $cIEmpty, $validatorInvalid, $allValues , !$isValid, $validatorMsg), - return array( - 'required-first-null' => array($requiredFirst, null), - 'required-last-null' => array($requiredLast, null), - 'required-first-empty' => array($requiredFirst, ''), - 'required-last-empty' => array($requiredLast, ''), - 'required-first-array' => array($requiredFirst, array()), - 'required-last-array' => array($requiredLast, array()), + 'Required: F; AEmpty: F; CIEmpty: F; Validator: X, Value: Empty' => array(!$isRequired, !$aEmpty, !$cIEmpty, $validatorNotCall, $emptyValues , $isValid, array()), + 'Required: F; AEmpty: F; CIEmpty: F; Validator: T, Value: Not Empty' => array(!$isRequired, !$aEmpty, !$cIEmpty, $validatorValid , $nonEmptyValues, $isValid, array()), + 'Required: F; AEmpty: F; CIEmpty: F; Validator: F, Value: Not Empty' => array(!$isRequired, !$aEmpty, !$cIEmpty, $validatorInvalid, $nonEmptyValues, !$isValid, $validatorMsg), ); + // @codingStandardsIgnoreEnd + + // Expand data template matrix for each possible input value. + // Description => [$isRequired, $allowEmpty, $continueIfEmpty, $validator, $value, $expectedIsValid] + $dataSets = array(); + foreach ($dataTemplates as $dataTemplateDescription => $dataTemplate) { + foreach ($dataTemplate[4] as $valueDescription => $value) { + $tmpTemplate = $dataTemplate; + $tmpTemplate[3] = $dataTemplate[3]($value['filtered']); // Get validator mock for each data set + $tmpTemplate[4] = $value['raw']; // expand value + + $dataSets[$dataTemplateDescription . ' / ' . $valueDescription] = $tmpTemplate; + } + } + + return $dataSets; } - /** - * @group 7448 - * @dataProvider whenNotRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun - */ - public function testWhenNotRequiredAndAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun($input, $value) + public function emptyValueProvider() { - $input->setValue($value); - $this->assertTrue($input->isValid()); + return array( + // Description => [$value] + 'null' => array( + 'raw' => null, + 'filtered' => null, + ), + '""' => array( + 'raw' => '', + 'filtered' => '', + ), +// '"0"' => array('0'), +// '0' => array(0), +// '0.0' => array(0.0), +// 'false' => array(false), + '[]' => array( + 'raw' => array(), + 'filtered' => array(), + ), + ); } - public function whenNotRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun() + public function mixedValueProvider() { - $validator = new Validator\Callback(function ($value) { - throw new RuntimeException('Validator executed when it should not be'); - }); - - $requiredFirst = new Input('foo'); - $requiredFirst->setRequired(false) - ->setAllowEmpty(false) - ->setContinueIfEmpty(false) - ->getValidatorChain()->attach($validator); - - $requiredLast = new Input('foo'); - $requiredLast->setAllowEmpty(false) - ->setContinueIfEmpty(false) - ->setRequired(false) - ->getValidatorChain()->attach($validator); - return array( - 'required-first-null' => array($requiredFirst, null), - 'required-last-null' => array($requiredLast, null), - 'required-first-empty' => array($requiredFirst, ''), - 'required-last-empty' => array($requiredLast, ''), - 'required-first-array' => array($requiredFirst, array()), - 'required-last-array' => array($requiredLast, array()), + // Description => [$value] + '"0"' => array( + 'raw' => '0', + 'filtered' => '0', + ), + '0' => array( + 'raw' => 0, + 'filtered' => 0, + ), + '0.0' => array( + 'raw' => 0.0, + 'filtered' => 0.0, + ), +// TODO enable me +// 'false' => array( +// 'raw' => false, +// 'filtered' => false, +// ), + 'php' => array( + 'raw' => 'php', + 'filtered' => 'php', + ), +// TODO enable me +// 'whitespace' => array( +// 'raw' => ' ', +// 'filtered' => ' ', +// ), + '1' => array( + 'raw' => 1, + 'filtered' => 1, + ), + '1.0' => array( + 'raw' => 1.0, + 'filtered' => 1.0, + ), + 'true' => array( + 'raw' => true, + 'filtered' => true, + ), + '["php"]' => array( + 'raw' => array('php'), + 'filtered' => array('php'), + ), + 'object' => array( + 'raw' => new stdClass(), + 'filtered' => new stdClass(), + ), + // @codingStandardsIgnoreStart +// TODO Skip HHVM failure enable me +// 'callable' => array( +// 'raw' => function () {}, +// 'filtered' => function () {}, +// ), + // @codingStandardsIgnoreEnd ); } /** - * @group 7448 - * @dataProvider whenNotRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun + * @return InputInterface|MockObject */ - public function testWhenNotRequiredAndNotAllowEmptyAndNotContinueIfEmptyValidatorsAreNotRun($input, $value) + public function createInputInterfaceMock() { - $input->setValue($value); - $this->assertTrue($input->isValid()); + /** @var InputInterface|MockObject $source */ + $source = $this->getMock('Zend\InputFilter\InputInterface'); + + return $source; } - public function whenNotRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun() + /** + * @param mixed $valueRaw + * @param mixed $valueFiltered + * + * @return FilterChain|MockObject + */ + public function createFilterChainMock($valueRaw = null, $valueFiltered = null) { - $alwaysInvalid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return false; - }); + /** @var FilterChain|MockObject $filterChain */ + $filterChain = $this->getMock('Zend\Filter\FilterChain'); - $emptyIsValid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return true; - }); - - $requiredFirstInvalid = new Input('foo'); - $requiredFirstValid = new Input('foo'); - foreach (array($requiredFirstValid, $requiredFirstInvalid) as $input) { - $input->setRequired(false) - ->setAllowEmpty(true) - ->setContinueIfEmpty(true); - } + $filterChain->method('filter') + ->with($valueRaw) + ->willReturn($valueFiltered) + ; - $requiredLastInvalid = new Input('foo'); - $requiredLastValid = new Input('foo'); - foreach (array($requiredLastValid, $requiredLastInvalid) as $input) { - $input->setAllowEmpty(true) - ->setContinueIfEmpty(true) - ->setRequired(false); - } + return $filterChain; + } - foreach (array($requiredFirstValid, $requiredLastValid) as $input) { - $input->getValidatorChain()->attach($emptyIsValid); + /** + * @param null|bool $isValid If set stub isValid method for return the argument value. + * @param mixed $value + * @param mixed $context + * @param string[] $messages + * + * @return ValidatorChain|MockObject + */ + public function createValidatorChainMock($isValid = null, $value = null, $context = null, $messages = array()) + { + /** @var ValidatorChain|MockObject $validatorChain */ + $validatorChain = $this->getMock('Zend\Validator\ValidatorChain'); + + if (($isValid === false) || ($isValid === true)) { + $validatorChain->expects($this->once()) + ->method('isValid') + ->with($value, $context) + ->willReturn($isValid) + ; + } else { + $validatorChain->expects($this->never()) + ->method('isValid') + ->with($value, $context) + ; } - foreach (array($requiredFirstInvalid, $requiredLastInvalid) as $input) { - $input->getValidatorChain()->attach($alwaysInvalid); - } + $validatorChain->method('getMessages') + ->willReturn($messages) + ; - return array( - 'required-first-null-valid' => array($requiredFirstValid, null, 'assertTrue'), - 'required-first-null-invalid' => array($requiredFirstInvalid, null, 'assertFalse'), - 'required-last-null-valid' => array($requiredLastValid, null, 'assertTrue'), - 'required-last-null-invalid' => array($requiredLastInvalid, null, 'assertFalse'), - 'required-first-empty-valid' => array($requiredFirstValid, '', 'assertTrue'), - 'required-first-empty-invalid' => array($requiredFirstInvalid, '', 'assertFalse'), - 'required-last-empty-valid' => array($requiredLastValid, '', 'assertTrue'), - 'required-last-empty-invalid' => array($requiredLastInvalid, '', 'assertFalse'), - 'required-first-array-valid' => array($requiredFirstValid, array(), 'assertTrue'), - 'required-first-array-invalid' => array($requiredFirstInvalid, array(), 'assertFalse'), - 'required-last-array-valid' => array($requiredLastValid, array(), 'assertTrue'), - 'required-last-array-invalid' => array($requiredLastInvalid, array(), 'assertFalse'), - ); + return $validatorChain; } /** - * @group 7448 - * @dataProvider whenNotRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun + * @param null|bool $isValid + * @param mixed $value + * @param mixed $context + * @param string[] $messages + * + * @return ValidatorInterface|MockObject */ - public function testWhenNotRequiredAndAllowEmptyAndContinueIfEmptyValidatorsAreRun($input, $value, $assertion) - { - $input->setValue($value); - $this->{$assertion}($input->isValid()); - } - - public function whenNotRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun() - { - $alwaysInvalid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return false; - }); - - $emptyIsValid = new Validator\Callback(function ($value) { - if (! empty($value)) { - throw new RuntimeException('Unexpected non-empty value provided to validate'); - } - return true; - }); - - $requiredFirstInvalid = new Input('foo'); - $requiredFirstValid = new Input('foo'); - foreach (array($requiredFirstValid, $requiredFirstInvalid) as $input) { - $input->setRequired(false) - ->setAllowEmpty(false) - ->setContinueIfEmpty(true); + public function createValidatorMock($isValid, $value = 'not-set', $context = null, $messages = array()) + { + /** @var ValidatorInterface|MockObject $validator */ + $validator = $this->getMock('Zend\Validator\ValidatorInterface'); + + if (($isValid === false) || ($isValid === true)) { + $isValidMethod = $validator->expects($this->once()) + ->method('isValid') + ->willReturn($isValid) + ; + } else { + $isValidMethod = $validator->expects($this->never()) + ->method('isValid') + ; } - - $requiredLastInvalid = new Input('foo'); - $requiredLastValid = new Input('foo'); - foreach (array($requiredLastValid, $requiredLastInvalid) as $input) { - $input->setAllowEmpty(false) - ->setContinueIfEmpty(true) - ->setRequired(false); + if ($value !== 'not-set') { + $isValidMethod->with($value, $context); } - foreach (array($requiredFirstValid, $requiredLastValid) as $input) { - $input->getValidatorChain()->attach($emptyIsValid); - } + $validator->method('getMessages') + ->willReturn($messages) + ; - foreach (array($requiredFirstInvalid, $requiredLastInvalid) as $input) { - $input->getValidatorChain()->attach($alwaysInvalid); - } - - return array( - 'required-first-null-valid' => array($requiredFirstValid, null, 'assertTrue'), - 'required-first-null-invalid' => array($requiredFirstInvalid, null, 'assertFalse'), - 'required-last-null-valid' => array($requiredLastValid, null, 'assertTrue'), - 'required-last-null-invalid' => array($requiredLastInvalid, null, 'assertFalse'), - 'required-first-empty-valid' => array($requiredFirstValid, '', 'assertTrue'), - 'required-first-empty-invalid' => array($requiredFirstInvalid, '', 'assertFalse'), - 'required-last-empty-valid' => array($requiredLastValid, '', 'assertTrue'), - 'required-last-empty-invalid' => array($requiredLastInvalid, '', 'assertFalse'), - 'required-first-array-valid' => array($requiredFirstValid, array(), 'assertTrue'), - 'required-first-array-invalid' => array($requiredFirstInvalid, array(), 'assertFalse'), - 'required-last-array-valid' => array($requiredLastValid, array(), 'assertTrue'), - 'required-last-array-invalid' => array($requiredLastInvalid, array(), 'assertFalse'), - ); + return $validator; } /** - * @group 7448 - * @dataProvider whenNotRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun + * @param bool $isValid + * @param mixed $value + * @param mixed $context + * + * @return NotEmptyValidator|MockObject */ - public function testWhenNotRequiredAndNotAllowEmptyAndContinueIfEmptyValidatorsAreRun($input, $value, $assertion) + public function createNonEmptyValidatorMock($isValid, $value, $context = null) { - $input->setValue($value); - $this->{$assertion}($input->isValid()); + /** @var NotEmptyValidator|MockObject $notEmptyMock */ + $notEmptyMock = $this->getMock('Zend\Validator\NotEmpty', array('isValid')); + $notEmptyMock->expects($this->once()) + ->method('isValid') + ->with($value, $context) + ->willReturn($isValid) + ; + + return $notEmptyMock; + } + + public function getDummyValue($raw = true) + { + if ($raw) { + return 'foo'; + } + return 'filtered'; } }