Files
webmozarts-assert/tests/AssertTest.php
T

1130 lines
49 KiB
PHP

<?php
declare(strict_types=1);
/*
* This file is part of the webmozart/assert package.
*
* (c) Bernhard Schussek <bschussek@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Webmozart\Assert\Tests;
use ArrayIterator;
use ArrayObject;
use DateTime;
use DateTimeImmutable;
use Error;
use Exception;
use LogicException;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use SimpleXMLElement;
use stdClass;
use Webmozart\Assert\Assert;
/**
* @since 1.0
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class AssertTest extends TestCase
{
private const SKIP_MIXIN_ASSERTION_TESTS = [
'isInitialized',
];
private const SKIP_CUSTOM_MESSAGE_TESTS = [
'isAOf',
'isAnyOf',
'isNotA',
'startsWithLetter',
];
public static function getResource()
{
static $resource;
return $resource ??= tmpfile();
}
public static function getTests(): array
{
$nanList = ['key' => 2, NAN];
unset($nanList['key']);
$normalList = ['foo' => 'b', 3];
unset($normalList['foo']);
$a = new ToStringClass('testString');
$b = new ToStringClass('testString');
$c = new ToStringClass('otherString');
return [
['string', ['value'], true],
['string', [''], true],
['string', [1234], false],
['stringNotEmpty', ['value'], true],
['stringNotEmpty', ['0'], true],
['stringNotEmpty', [''], false],
['stringNotEmpty', [1234], false],
['integer', [123], true],
['integer', ['123'], false],
['integer', [1.0], false],
['integer', [1.23], false],
['integerish', [1.0], true],
['integerish', [1.23], false],
['integerish', [123], true],
['integerish', ['123'], true],
['positiveInteger', [123], true],
['positiveInteger', [1], true],
['positiveInteger', [-123], false],
['positiveInteger', [0], false],
['positiveInteger', [0.0], false],
['positiveInteger', ['123'], false],
['positiveInteger', ['-123'], false],
['positiveInteger', ['0'], false],
['positiveInteger', [1.0], false],
['positiveInteger', [1.23], false],
['notNegativeInteger', [123], true],
['notNegativeInteger', [1], true],
['notNegativeInteger', [-123], false],
['notNegativeInteger', [0], true],
['notNegativeInteger', [0.0], false],
['notNegativeInteger', ['123'], false],
['notNegativeInteger', ['-123'], false],
['notNegativeInteger', ['0'], false],
['notNegativeInteger', [1.0], false],
['notNegativeInteger', [1.23], false],
['negativeInteger', [123], false],
['negativeInteger', [1], false],
['negativeInteger', [-123], true],
['negativeInteger', [0], false],
['negativeInteger', [0.0], false],
['negativeInteger', ['123'], false],
['negativeInteger', ['-123'], false],
['negativeInteger', ['0'], false],
['negativeInteger', [1.0], false],
['negativeInteger', [1.23], false],
['float', [1.0], true],
['float', [1.23], true],
['float', [123], false],
['float', ['123'], false],
['numeric', [1.0], true],
['numeric', [1.23], true],
['numeric', [123], true],
['numeric', ['123'], true],
['numeric', ['foo'], false],
['natural', [0], true],
['natural', [1], true],
['natural', [-1], false],
['natural', ['1'], false],
['natural', [1.0], false],
['natural', [1.23], false],
['boolean', [true], true],
['boolean', [false], true],
['boolean', [1], false],
['boolean', ['1'], false],
['scalar', ['1'], true],
['scalar', [123], true],
['scalar', [true], true],
['scalar', [null], false],
['scalar', [[]], false],
['scalar', [new stdClass()], false],
['object', [new stdClass()], true],
['object', [new RuntimeException()], true],
['object', [null], false],
['object', [true], false],
['object', [1], false],
['object', [[]], false],
['objectish', [new stdClass()], true],
['objectish', [new RuntimeException()], true],
['objectish', [null], false],
['objectish', [true], false],
['objectish', [1], false],
['objectish', [[]], false],
['resource', [self::getResource()], true],
['resource', [self::getResource(), 'stream'], true],
['resource', [self::getResource(), 'other'], false],
['resource', [1], false],
['isInitialized', [new class {
public mixed $a = null;
}, 'a'], true],
['isInitialized', [new class {
public mixed $a;
}, 'a'], false],
['isInitialized', [new class {
public mixed $a;
public mixed $b = true;
}, 'a'], false],
['isInitialized', [new class {
public mixed $a;
public mixed $b = true;
}, 'b'], true],
['isCallable', ['strlen'], true],
['isCallable', [[self::class, 'getTests']], true],
['isCallable', [function () {}], true],
['isCallable', [1234], false],
['isCallable', ['foobar'], false],
['isArray', [[]], true],
['isArray', [[1, 2, 3]], true],
['isArray', [new ArrayIterator([])], false],
['isArray', [123], false],
['isArray', [new stdClass()], false],
['isArrayAccessible', [[]], true],
['isArrayAccessible', [[1, 2, 3]], true],
['isArrayAccessible', [new ArrayObject([])], true],
['isArrayAccessible', [123], false],
['isArrayAccessible', [new stdClass()], false],
['isCountable', [[]], true],
['isCountable', [[1, 2]], true],
['isCountable', [new ArrayIterator([])], true],
['isCountable', [new stdClass()], false],
['isCountable', [new SimpleXMLElement('<foo>bar</foo>')], true],
['isCountable', ['abcd'], false],
['isCountable', [123], false],
['isIterable', [[]], true],
['isIterable', [[1, 2, 3]], true],
['isIterable', [new ArrayIterator([])], true],
['isIterable', [123], false],
['isIterable', [new stdClass()], false],
['isInstanceOf', [new stdClass(), 'stdClass'], true],
['isInstanceOf', [new Exception(), 'stdClass'], false],
['isInstanceOf', [123, 'stdClass'], false],
['isInstanceOf', [[], 'stdClass'], false],
['isInstanceOf', [null, 'stdClass'], false],
['notInstanceOf', [new stdClass(), 'stdClass'], false],
['notInstanceOf', [new Exception(), 'stdClass'], true],
['notInstanceOf', [123, 'stdClass'], false],
['notInstanceOf', [[], 'stdClass'], false],
['notInstanceOf', [null, 'stdClass'], false],
['isInstanceOfAny', [new ArrayIterator(), ['Iterator', 'ArrayAccess']], true],
['isInstanceOfAny', [new Exception(), ['Exception', 'Countable']], true],
['isInstanceOfAny', [new Exception(), ['ArrayAccess', 'Countable']], false],
['isInstanceOfAny', [123, ['stdClass']], false],
['isInstanceOfAny', [[], ['stdClass']], false],
['isNotInstanceOfAny', [new ArrayIterator(), ['Exception', 'Countable']], false],
['isNotInstanceOfAny', [new Exception(), ['ArrayAccess', 'Countable']], true],
['isNotInstanceOfAny', [new Exception(), ['Exception', 'Countable']], false],
['isNotInstanceOfAny', [123, ['stdClass']], true],
['isNotInstanceOfAny', [[], ['stdClass']], true],
['isAOf', ['stdClass', 'stdClass'], true],
['isAOf', ['stdClass', 123], false],
['isAOf', ['Iterator', 'ArrayIterator'], false],
['isAOf', [123, 'Iterator'], false],
['isAOf', [[], 'Iterator'], false],
['isAnyOf', ['ArrayIterator', ['Iterator', 'ArrayAccess']], true],
['isAnyOf', ['ArrayIterator', [123]], false],
['isAnyOf', ['Exception', ['Exception', 'Countable']], true],
['isAnyOf', ['Exception', ['ArrayAccess', 'Countable']], false],
['isAnyOf', [123, ['stdClass']], false],
['isAnyOf', [[], ['stdClass']], false],
['isNotA', ['stdClass', 'stdClass'], false],
['isNotA', ['stdClass', 123], false],
['isNotA', [[], 'Iterator'], false],
['true', [true], true],
['true', [false], false],
['true', [1], false],
['true', [null], false],
['false', [false], true],
['false', [true], false],
['false', [1], false],
['false', [0], false],
['false', [null], false],
['notFalse', [false], false],
['notFalse', [true], true],
['notFalse', [1], true],
['notFalse', [0], true],
['notFalse', [null], true],
['null', [null], true],
['null', [false], false],
['null', [0], false],
['notNull', [false], true],
['notNull', [0], true],
['notNull', [null], false],
['isEmpty', [null], true],
['isEmpty', [false], true],
['isEmpty', [0], true],
['isEmpty', [''], true],
['isEmpty', [1], false],
['isEmpty', ['a'], false],
['notEmpty', [1], true],
['notEmpty', ['a'], true],
['notEmpty', [null], false],
['notEmpty', [false], false],
['notEmpty', [0], false],
['notEmpty', [''], false],
['eq', [1, 1], true],
['eq', [1, '1'], true],
['eq', [1, true], true],
['eq', [1, 0], false],
['eq', [$a, $a], true],
['eq', [$a, $b], true],
['eq', [$a, $c], false],
['notEq', [1, 0], true],
['notEq', [1, 1], false],
['notEq', [1, '1'], false],
['notEq', [1, true], false],
['same', [1, 1], true],
['same', [1, '1'], false],
['same', [1, true], false],
['same', [1, 0], false],
['same', [$a, $b], false],
['same', [$a, $a], true],
['notSame', [1, 0], true],
['notSame', [1, 1], false],
['notSame', [1, '1'], true],
['notSame', [1, true], true],
['notSame', [$a, $b], true],
['notSame', [$a, $a], false],
['greaterThan', [1, 0], true],
['greaterThan', [0, 0], false],
['greaterThanEq', [2, 1], true],
['greaterThanEq', [1, 1], true],
['greaterThanEq', [0, 1], false],
['lessThan', [0, 1], true],
['lessThan', [1, 1], false],
['lessThanEq', [0, 1], true],
['lessThanEq', [1, 1], true],
['lessThanEq', [2, 1], false],
['range', [1, 1, 2], true],
['range', [2, 1, 2], true],
['range', [0, 1, 2], false],
['range', [3, 1, 2], false],
['oneOf', [1, [1, 2, 3]], true],
['oneOf', [1, ['1', '2', '3']], false],
['notOneOf', [1, [1, 2, 3]], false],
['notOneOf', [1, ['1', '2', '3']], true],
['inArray', [1, [1, 2, 3]], true],
['inArray', [1, ['1', '2', '3']], false],
['inArray', [$a, [$a]], true],
['inArray', [$a, [(string) $a]], false],
['inArray', [$a, [$b]], false],
['inArray', [$a, [$c]], false],
['notInArray', [1, [1, 2, 3]], false],
['notInArray', [1, ['1', '2', '3']], true],
['notInArray', [$a, [$a]], false],
['notInArray', [$a, [$b]], true],
['notInArray', [$a, [$c]], true],
['contains', ['abcd', 'ab'], true],
['contains', ['abcd', 'bc'], true],
['contains', ['abcd', 'cd'], true],
['contains', ['abcd', 'de'], false],
['contains', ['', 'de'], false],
['contains', ['äþçð', 'äþ'], true],
['contains', ['äþçð', 'þç'], true],
['contains', ['äþçð', 'çð'], true],
['contains', ['äþçð', 'ðé'], false],
['contains', ['', 'ðé'], false],
['contains', ['あいうえ', 'あい'], true],
['contains', ['あいうえ', 'いう'], true],
['contains', ['あいうえ', 'うえ'], true],
['contains', ['あいうえ', 'えお'], false],
['contains', ['', 'えお'], false],
['contains', ['😄😑☹️', '😄'], true],
['contains', ['😄😑☹️', '😑'], true],
['contains', ['😄😑☹️', '☹️'], true],
['contains', ['😄😑☹️', '😄☹️'], false],
['contains', ['', '😑'], false],
['notContains', ['abcd', 'ab'], false],
['notContains', ['abcd', 'bc'], false],
['notContains', ['abcd', 'cd'], false],
['notContains', ['abcd', 'de'], true],
['notContains', ['', 'de'], true],
['notContains', ['äþçð', 'äþ'], false],
['notContains', ['äþçð', 'þç'], false],
['notContains', ['äþçð', 'çð'], false],
['notContains', ['äþçð', 'ðé'], true],
['notContains', ['', 'ðé'], true],
['notContains', ['あいうえ', 'あい'], false],
['notContains', ['あいうえ', 'いう'], false],
['notContains', ['あいうえ', 'うえ'], false],
['notContains', ['あいうえ', 'えお'], true],
['notContains', ['', 'えお'], true],
['notContains', ['😄😑☹️', '😄'], false],
['notContains', ['😄😑☹️', '😑'], false],
['notContains', ['😄😑☹️', '☹️'], false],
['notContains', ['😄😑☹️', '😄☹️'], true],
['notContains', ['', '😑'], true],
['notWhitespaceOnly', ['abc'], true],
['notWhitespaceOnly', ['123'], true],
['notWhitespaceOnly', [' abc '], true],
['notWhitespaceOnly', ['a b c'], true],
['notWhitespaceOnly', [''], false],
['notWhitespaceOnly', [' '], false],
['notWhitespaceOnly', ["\t"], false],
['notWhitespaceOnly', ["\n"], false],
['notWhitespaceOnly', ["\r"], false],
['notWhitespaceOnly', ["\r\n\t "], false],
['startsWith', ['abcd', 'ab'], true],
['startsWith', ['abcd', 'bc'], false],
['startsWith', ['', 'bc'], false],
['startsWith', ['äþçð', 'äþ'], true],
['startsWith', ['äþçð', 'þç'], false],
['startsWith', ['', 'þç'], false],
['startsWith', ['あいうえ', 'あい'], true],
['startsWith', ['あいうえ', 'いう'], false],
['startsWith', ['', 'いう'], false],
['startsWith', ['😄😑☹️', '😄'], true],
['startsWith', ['😄😑☹️', '😑'], false],
['startsWith', ['', '😑'], false],
['startsWithLetter', ['abcd'], true],
['startsWithLetter', [[66]], false],
['startsWithLetter', ['a'], true],
['startsWithLetter', ['a1'], true],
['startsWithLetter', ['1abcd'], false],
['startsWithLetter', ['1'], false],
['startsWithLetter', [''], false],
['startsWithLetter', [null], false],
['startsWithLetter', [66], false],
['notStartsWith', ['abcd', 'ab'], false],
['notStartsWith', ['abcd', 'bc'], true],
['notStartsWith', ['', 'bc'], true],
['notStartsWith', ['äþçð', 'äþ'], false],
['notStartsWith', ['äþçð', 'þç'], true],
['notStartsWith', ['', 'þç'], true],
['notStartsWith', ['あいうえ', 'あい'], false],
['notStartsWith', ['あいうえ', 'いう'], true],
['notStartsWith', ['', 'いう'], true],
['notStartsWith', ['😄😑☹️', '😄'], false],
['notStartsWith', ['😄😑☹️', '😑'], true],
['notStartsWith', ['', '😑'], true],
['endsWith', ['abcd', 'cd'], true],
['endsWith', ['abcd', 'bc'], false],
['endsWith', ['', 'bc'], false],
['endsWith', ['äþçð', 'çð'], true],
['endsWith', ['äþçð', 'þç'], false],
['endsWith', ['', 'þç'], false],
['endsWith', ['あいうえ', 'うえ'], true],
['endsWith', ['あいうえ', 'いう'], false],
['endsWith', ['', 'いう'], false],
['endsWith', ['😄😑☹️', '☹️'], true],
['endsWith', ['😄😑☹️', '😑'], false],
['endsWith', ['', '😑'], false],
['notEndsWith', ['abcd', 'cd'], false],
['notEndsWith', ['abcd', 'bc'], true],
['notEndsWith', ['', 'bc'], true],
['notEndsWith', ['äþçð', 'çð'], false],
['notEndsWith', ['äþçð', 'þç'], true],
['notEndsWith', ['', 'þç'], true],
['notEndsWith', ['あいうえ', 'うえ'], false],
['notEndsWith', ['あいうえ', 'いう'], true],
['notEndsWith', ['', 'いう'], true],
['notEndsWith', ['😄😑☹️', '☹️'], false],
['notEndsWith', ['😄😑☹️', '😑'], true],
['notEndsWith', ['', '😑'], true],
['regex', ['abcd', '~^ab~'], true],
['regex', ['abcd', '~^bc~'], false],
['regex', ['', '~^bc~'], false],
['notRegex', ['abcd', '{^ab}'], false],
['notRegex', ['abcd', '{^bc}'], true],
['notRegex', ['', '{^bc}'], true],
['unicodeLetters', ['abcd'], true],
['unicodeLetters', ['ᴁڅਘธブ乶'], true],
['unicodeLetters', ['ȁȄ'], true],
['unicodeLetters', ['ȁ1Ȅ'], false],
['unicodeLetters', ['©'], false],
['unicodeLetters', ['🙁'], false],
['unicodeLetters', [''], false],
['alpha', ['abcd'], true],
['alpha', ['ab1cd'], false],
['alpha', [''], false],
['alpha', [66], false],
['alpha', [[]], false],
['digits', ['1234'], true],
['digits', ['12a34'], false],
['digits', [''], false],
['digits', [1234], false],
['alnum', ['ab12'], true],
['alnum', ['ab12$'], false],
['alnum', [''], false],
['alnum', [1234], false],
['lower', ['abcd'], true],
['lower', ['abCd'], false],
['lower', ['ab_d'], false],
['lower', [''], false],
['lower', [1234], false],
['upper', ['ABCD'], true],
['upper', ['ABcD'], false],
['upper', ['AB_D'], false],
['upper', [''], false],
['upper', [1234], false],
['length', ['abcd', 4], true],
['length', ['abc', 4], false],
['length', ['abcde', 4], false],
['length', ['äbcd', 4], true, true],
['length', ['äbc', 4], false, true],
['length', ['äbcde', 4], false, true],
['length', ['あbcd', 4], true, true], // 'HIRAGANA LETTER A' (U+3042)
['length', ['あbc', 4], false, true],
['length', ['あbcde', 4], false, true],
['minLength', ['abcd', 4], true],
['minLength', ['abcde', 4], true],
['minLength', ['abc', 4], false],
['minLength', ['äbcd', 4], true, true],
['minLength', ['äbcde', 4], true, true],
['minLength', ['äbc', 4], false, true],
['minLength', ['あbcd', 4], true, true],
['minLength', ['あbcde', 4], true, true],
['minLength', ['あbc', 4], false, true],
['maxLength', ['abcd', 4], true],
['maxLength', ['abc', 4], true],
['maxLength', ['abcde', 4], false],
['maxLength', ['äbcd', 4], true, true],
['maxLength', ['äbc', 4], true, true],
['maxLength', ['äbcde', 4], false, true],
['maxLength', ['あbcd', 4], true, true],
['maxLength', ['あbc', 4], true, true],
['maxLength', ['あbcde', 4], false, true],
['lengthBetween', ['abcd', 3, 5], true],
['lengthBetween', ['abc', 3, 5], true],
['lengthBetween', ['abcde', 3, 5], true],
['lengthBetween', ['ab', 3, 5], false],
['lengthBetween', ['abcdef', 3, 5], false],
['lengthBetween', ['äbcd', 3, 5], true, true],
['lengthBetween', ['äbc', 3, 5], true, true],
['lengthBetween', ['äbcde', 3, 5], true, true],
['lengthBetween', ['äb', 3, 5], false, true],
['lengthBetween', ['äbcdef', 3, 5], false, true],
['lengthBetween', ['あbcd', 3, 5], true, true],
['lengthBetween', ['あbc', 3, 5], true, true],
['lengthBetween', ['あbcde', 3, 5], true, true],
['lengthBetween', ['あb', 3, 5], false, true],
['lengthBetween', ['あbcdef', 3, 5], false, true],
['fileExists', [__FILE__], true],
['fileExists', [__DIR__], true],
['fileExists', [__DIR__.'/foobar'], false],
['file', [__FILE__], true],
['file', [__DIR__], false],
['file', [__DIR__.'/foobar'], false],
['directory', [__DIR__], true],
['directory', [__FILE__], false],
['directory', [__DIR__.'/foobar'], false],
// no tests for readable()/writable() for now
['classExists', [__CLASS__], true],
['classExists', [__NAMESPACE__.'\Foobar'], false],
['subclassOf', [__CLASS__, 'PHPUnit\Framework\TestCase'], true],
['subclassOf', [__CLASS__, 'stdClass'], false],
['interfaceExists', ['\Countable'], true],
['interfaceExists', [__CLASS__], false],
['implementsInterface', ['ArrayIterator', 'Traversable'], true],
['implementsInterface', [__CLASS__, 'Traversable'], false],
['implementsInterface', [new DateTimeImmutable(), 'DateTimeInterface'], true],
['implementsInterface', [new DateTimeImmutable(), 'Traversable'], false],
['implementsInterface', [new ArrayIterator([]), 'DateTimeInterface'], false],
['implementsInterface', [new ArrayIterator([]), 'Traversable'], true],
['propertyExists', [(object) ['property' => 0], 'property'], true],
['propertyExists', [(object) ['property' => null], 'property'], true],
['propertyExists', [(object) ['property' => null], 'foo'], false],
['propertyNotExists', [(object) ['property' => 0], 'property'], false],
['propertyNotExists', [(object) ['property' => null], 'property'], false],
['methodExists', ['RuntimeException', 'getMessage'], true],
['methodExists', [new RuntimeException(), 'getMessage'], true],
['methodExists', ['stdClass', 'getMessage'], false],
['methodExists', [new stdClass(), 'getMessage'], false],
['methodNotExists', ['RuntimeException', 'getMessage'], false],
['methodNotExists', [new RuntimeException(), 'getMessage'], false],
['methodNotExists', ['stdClass', 'getMessage'], true],
['methodNotExists', [new stdClass(), 'getMessage'], true],
['keyExists', [['key' => 0], 'key'], true],
['keyExists', [['key' => null], 'key'], true],
['keyExists', [['key' => null], 'foo'], false],
['keyNotExists', [['key' => 0], 'key'], false],
['keyNotExists', [['key' => null], 'key'], false],
['keyNotExists', [['key' => null], 'foo'], true],
['validArrayKey', ['abcd'], true],
['validArrayKey', [1], true],
['validArrayKey', [false], false],
['validArrayKey', [true], false],
['validArrayKey', [new stdClass()], false],
['validArrayKey', [new ToStringClass('testString')], false],
['validArrayKey', [self::getResource()], false],
['count', [[0, 1, 2], 3], true],
['count', [[0, 1, 2], 2], false],
['minCount', [[0], 2], false],
['minCount', [[0, 1], 2], true],
['minCount', [[0, 1, 2], 2], true],
['maxCount', [[0, 1, 2], 2], false],
['maxCount', [[0, 1], 2], true],
['maxCount', [[0], 2], true],
['countBetween', [[0, 1, 2], 4, 5], false],
['countBetween', [[0, 1, 2], 3, 5], true],
['countBetween', [[0, 1, 2], 1, 2], false],
['countBetween', [[0, 1, 2], 2, 5], true],
['countBetween', [[0, 1, 2], 2, 3], true],
['isList', [[1, 2, 3]], true],
['isList', [[]], true],
['isList', [[0 => 1, 2 => 3]], false],
['isList', [['key' => 1, 'foo' => 2]], false],
['isList', [true], false],
['isList', [false], false],
['isList', [[true]], true],
['isList', [[false]], true],
['isList', [[[1], [2]]], true],
['isList', [[['foo' => 'bar'], ['baz' => 'tab']]], true],
['isList', [$nanList], true],
['isList', [$normalList], true],
['isNonEmptyList', [[1, 2, 3]], true],
['isNonEmptyList', [[]], false],
['isNonEmptyList', [[0 => 1, 2 => 3]], false],
['isNonEmptyList', [['key' => 1, 'foo' => 2]], false],
['isNonEmptyList', [true], false],
['isNonEmptyList', [false], false],
['isNonEmptyList', [[true]], true],
['isNonEmptyList', [[false]], true],
['isNonEmptyList', [[[1], [2]]], true],
['isNonEmptyList', [[['foo' => 'bar'], ['baz' => 'tab']]], true],
['isMap', [['key' => 1, 'foo' => 2]], true],
['isMap', [[0 => 1, 2 => 3]], true],
['isMap', [[]], true],
['isMap', [[1, 2, 3]], false],
['isNonEmptyMap', [['key' => 1, 'foo' => 2]], true],
['isNonEmptyMap', [[0 => 1, 2 => 3]], true],
['isNonEmptyMap', [[]], false],
['isNonEmptyMap', [[1, 2, 3]], false],
['uuid', ['00000000-0000-0000-0000-000000000000'], true],
['uuid', ['urn:uuid:ff6f8cb0-c57d-21e1-9b21-0800200c9a66'], true],
['uuid', ['uuid:{ff6f8cb0-c57d-21e1-9b21-0800200c9a66}'], true],
['uuid', ['{ff6f8cb0-c57d-21e1-9b21-0800200c9a66}'], true],
['uuid', ['ff6f8cb0-c57d-21e1-9b21-0800200c9a66'], true],
['uuid', ['ff6f8cb0-c57d-11e1-9b21-0800200c9a66'], true],
['uuid', ['ff6f8cb0-c57d-31e1-9b21-0800200c9a66'], true],
['uuid', ['ff6f8cb0-c57d-41e1-9b21-0800200c9a66'], true],
['uuid', ['ff6f8cb0-c57d-51e1-9b21-0800200c9a66'], true],
['uuid', ['FF6F8CB0-C57D-11E1-9B21-0800200C9A66'], true],
['uuid', ["urn:ff6f8cb0-c57d-21e1-9b21-0800200c9a66\n"], false],
['uuid', ["ff6f8cb0-c57d-21e1-9b21-0800200c9a66\n"], false],
['uuid', ["FF6F8CB0-C57D-11E1-9B21-0800200C9A66\n"], false],
['uuid', ['zf6f8cb0-c57d-11e1-9b21-0800200c9a66'], false],
['uuid', ['af6f8cb0c57d11e19b210800200c9a66'], false],
['uuid', ['ff6f8cb0-c57da-51e1-9b21-0800200c9a66'], false],
['uuid', ['af6f8cb-c57d-11e1-9b21-0800200c9a66'], false],
['uuid', ['3f6f8cb0-c57d-11e1-9b21-0800200c9a6'], false],
['uuid', ['f{}f6f8cb0-c57d-21e1-9b21-0800200c9a66'], false],
['uuid', ['ff6f8cb0-c57d-21e1-9b21-08002urn:00c9a66'], false],
['uuid', ['urn:ff6f8cb0-c57d-21e1-9b21-0800200c9a66uuid:'], false],
['uuid', ['urn:ff6f8cb0-c57d-21e1-9b21-0800200c9a66'], false],
['uuid', ['urn:uuid:{ff6f8cb0-c57d-21e1-9b21-0800200c9a66}'], false],
['uuid', ['{}{}ff6f8cb0-c57d-21e1-9b21-0800200c9a66{}{}'], false],
['uuid', ['{ff6f8cb0-c57d-21e1-9b21-0800200c9a66'], false],
['uuid', ['ff6f8cb0-c57d-21e1-9b21-0800200c9a66}'], false],
['throws', [function () { throw new LogicException('test'); }, 'LogicException'], true],
['throws', [function () { throw new LogicException('test'); }, 'IllogicException'], false],
['throws', [function () { throw new Exception('test'); }], true],
['throws', [function () { trigger_error('test'); }, 'Throwable'], false, false],
['throws', [function () { trigger_error('test'); }, 'Unthrowable'], false, false],
['throws', [function () { throw new Error(); }, 'Throwable'], true, true],
['ip', ['192.168.0.1'], true],
['ip', ['255.255.255.255'], true],
['ip', ['0.0.0.0'], true],
['ip', ['2001:0db8:0000:0042:0000:8a2e:0370:7334'], true],
['ip', ['::ffff:192.0.2.1'], true],
['ip', ['::1'], true],
['ip', ['::'], true],
['ip', ['foo'], false],
['ip', [123], false],
['ip', [[]], false],
['ip', [null], false],
['ip', [false], false],
['ipv4', ['192.168.0.1'], true],
['ipv4', ['255.255.255.255'], true],
['ipv4', ['0.0.0.0'], true],
['ipv4', ['2001:0db8:0000:0042:0000:8a2e:0370:7334'], false],
['ipv4', ['::ffff:192.0.2.1'], false],
['ipv4', ['::1'], false],
['ipv4', ['::'], false],
['ipv4', ['foo'], false],
['ipv4', [123], false],
['ipv4', [[]], false],
['ipv4', [null], false],
['ipv4', [false], false],
['ipv6', ['192.168.0.1'], false],
['ipv6', ['255.255.255.255'], false],
['ipv6', ['0.0.0.0'], false],
['ipv6', ['2001:0db8:0000:0042:0000:8a2e:0370:7334'], true],
['ipv6', ['::ffff:192.0.2.1'], true],
['ipv6', ['::1'], true],
['ipv6', ['::'], true],
['ipv6', ['foo'], false],
['ipv6', [123], false],
['ipv6', [[]], false],
['ipv6', [null], false],
['ipv6', [false], false],
['email', ['foo'], false],
['email', [123], false],
['email', ['foo.com'], false],
['email', ['foo@bar.com'], true],
['email', ['föö@bar.com'], true],
['uniqueValues', [['qwerty', 'qwerty']], false],
['uniqueValues', [['asdfg', 'qwerty']], true],
['uniqueValues', [[123, '123']], false],
['uniqueValues', [[$a, $a]], false],
['uniqueValues', [[$a, $b]], false],
['uniqueValues', [[$a, $c]], true],
['isStatic', [static function () {}], true],
['isStatic', [function () {}], false],
['notStatic', [static function () {}], false],
['notStatic', [function () {}], true],
];
}
public static function getMethods(): array
{
$methods = [];
foreach (self::getTests() as $params) {
$methods[$params[0]] = [$params[0]];
}
return array_values($methods);
}
#[DataProvider('getTests')]
public function testAssert(string $method, array $args, bool $success, bool $multibyte = false): void
{
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
}
$result = call_user_func_array(['Webmozart\Assert\Assert', $method], $args);
$this->assertSame($args[array_key_first($args)], $result);
}
#[DataProvider('getTests')]
public function testCustomMessage(string $method, array $args, bool $success, bool $multibyte = false): void
{
if (in_array($method, self::SKIP_CUSTOM_MESSAGE_TESTS)) {
$this->markTestSkipped("The method $method could have specific message handling instead of custom message.");
}
if ($args === []) {
$this->addToAssertionCount(1);
return;
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage('Custom error message');
}
$args['message'] = 'Custom error message';
$result = Assert::$method(...$args);
$this->assertSame($args[array_key_first($args)], $result);
}
#[DataProvider('getTests')]
public function testLazyMessageCallbackCalled(string $method, array $args, bool $success, bool $multibyte = false): void
{
if ($args === []) {
$this->addToAssertionCount(1);
return;
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
}
$called = 0;
$args['message'] = function () use (&$called) {
++$called;
return 'Custom error message number';
};
Assert::$method(...$args);
$expectedCalled = $success ? 0 : 1;
$this->assertSame($expectedCalled, $called, sprintf('The lazy message callback should be called exactly %d times.', $expectedCalled));
}
#[DataProvider('getTests')]
public function testNullOr(string $method, array $args, bool $success, bool $multibyte = false): void
{
if (in_array($method, self::SKIP_MIXIN_ASSERTION_TESTS)) {
$this->markTestSkipped("The method $method does not have nullOr Mixin.");
}
if (in_array($method, ['null', 'notNull'])) {
$this->markTestSkipped('Meaningless test of '.$method);
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success && null !== reset($args)) {
$this->expectException('\InvalidArgumentException');
}
$result = call_user_func_array(['Webmozart\Assert\Assert', 'nullOr'.ucfirst($method)], $args);
$this->assertSame($args[array_key_first($args)], $result);
}
#[DataProvider('getMethods')]
public function testNullOrAcceptsNull(string $method): void
{
if (in_array($method, self::SKIP_MIXIN_ASSERTION_TESTS)) {
$this->markTestSkipped("The method $method does not have nullOr Mixin.");
}
if (in_array($method, ['null', 'notNull'])) {
$this->markTestSkipped('Meaningless test of '.$method);
}
$result = call_user_func(['Webmozart\Assert\Assert', 'nullOr'.ucfirst($method)], null, '', '');
$this->assertNull($result);
}
#[DataProvider('getTests')]
public function testAllArray(string $method, array $args, bool $success, bool $multibyte = false): void
{
if (in_array($method, self::SKIP_MIXIN_ASSERTION_TESTS)) {
$this->markTestSkipped("The method $method does not have all Mixin.");
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
}
$arg = array_shift($args);
array_unshift($args, [$arg]);
$result = call_user_func_array(['Webmozart\Assert\Assert', 'all'.ucfirst($method)], $args);
$this->assertSame($args[array_key_first($args)], $result);
}
#[DataProvider('getTests')]
public function testAllNullOrArray(string $method, array $args, bool $success, bool $multibyte = false): void
{
if (in_array($method, self::SKIP_MIXIN_ASSERTION_TESTS)) {
$this->markTestSkipped("The method $method does not have allNullOr Mixin.");
}
if (in_array($method, ['null', 'notNull'])) {
$this->markTestSkipped('Meaningless test of '.$method);
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
$arg = array_shift($args);
if ($arg === null) {
$this->addToAssertionCount(1);
return;
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
}
array_unshift($args, [$arg, null]);
$result = call_user_func_array(['Webmozart\Assert\Assert', 'allNullOr'.ucfirst($method)], $args);
$this->assertSame($args[array_key_first($args)], $result);
}
#[DataProvider('getTests')]
public function testAllTraversable(string $method, array $args, bool $success, bool $multibyte = false): void
{
if (in_array($method, self::SKIP_MIXIN_ASSERTION_TESTS)) {
$this->markTestSkipped("The method $method does not have all Mixin.");
}
if ($multibyte && !function_exists('mb_strlen')) {
$this->markTestSkipped('The function mb_strlen() is not available');
}
if (!$success) {
$this->expectException('\InvalidArgumentException');
}
$arg = array_shift($args);
array_unshift($args, new ArrayIterator([$arg]));
$result = call_user_func_array(['Webmozart\Assert\Assert', 'all'.ucfirst($method)], $args);
$this->assertSame($args[array_key_first($args)], $result);
}
public static function getStringConversions(): array
{
return [
['integer', ['foobar'], 'Expected an integer. Got: string'],
['string', [1], 'Expected a string. Got: integer'],
['string', [true], 'Expected a string. Got: boolean'],
['string', [null], 'Expected a string. Got: NULL'],
['string', [[]], 'Expected a string. Got: array'],
['string', [new stdClass()], 'Expected a string. Got: stdClass'],
['string', [self::getResource()], 'Expected a string. Got: resource'],
['eq', ['1', '2'], 'Expected a value equal to "2". Got: "1"'],
['eq', [new ToStringClass('XXX'), new ToStringClass('YYY')], 'Expected a value equal to Webmozart\Assert\Tests\ToStringClass: "YYY". Got: Webmozart\Assert\Tests\ToStringClass: "XXX"'],
['eq', [1, 2], 'Expected a value equal to 2. Got: 1'],
['eq', [true, false], 'Expected a value equal to false. Got: true'],
['eq', [true, null], 'Expected a value equal to null. Got: true'],
['eq', [null, true], 'Expected a value equal to true. Got: null'],
['eq', [[1], [2]], 'Expected a value equal to array. Got: array'],
['eq', [new ArrayIterator([]), new stdClass()], 'Expected a value equal to stdClass. Got: ArrayIterator'],
['eq', [1, self::getResource()], 'Expected a value equal to resource. Got: 1'],
['lessThan', [new DateTime('2020-01-01 00:00:00+00:00'), new DateTime('1999-01-01 00:00:00+00:00')], 'Expected a value less than DateTime: "1999-01-01T00:00:00+00:00". Got: DateTime: "2020-01-01T00:00:00+00:00"'],
];
}
#[DataProvider('getStringConversions')]
public function testConvertValuesToStrings(string $method, array $args, string $exceptionMessage): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage($exceptionMessage);
call_user_func_array(['Webmozart\Assert\Assert', $method], $args);
}
public static function getInvalidInstanceOfCases(): iterable
{
yield [
[null, 'stdClass'],
'Expected an instance of stdClass. Got: NULL',
];
yield [
[123, 'stdClass'],
'Expected an instance of stdClass. Got: integer',
];
yield [
[[], 'Iterator'],
'Expected an instance of Iterator. Got: array',
];
yield [
[new stdClass(), 'Iterator'],
'Expected an instance of Iterator. Got: stdClass',
];
}
#[DataProvider('getInvalidInstanceOfCases')]
public function testInstanceOfExceptionMessages(array $args, string $exceptionMessage): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage($exceptionMessage);
call_user_func_array(['Webmozart\Assert\Assert', 'isInstanceOf'], $args);
}
public static function getInvalidIsAOfCases(): iterable
{
yield [
['stdClass', 123],
'Expected class as a string. Got: integer',
];
yield [
['Iterator', 'ArrayIterator'],
'Expected an instance of this class or to this class among its parents "ArrayIterator". Got: "Iterator"',
];
yield [
[123, 'Iterator'],
'Expected an instance of this class or to this class among its parents "Iterator". Got: 123',
];
yield [
[[], 'Iterator'],
'Expected an instance of this class or to this class among its parents "Iterator". Got: array',
];
yield [
[new stdClass(), 'Iterator'],
'Expected an instance of this class or to this class among its parents "Iterator". Got: stdClass',
];
}
#[DataProvider('getInvalidIsAOfCases')]
public function testIsAOfExceptionMessages(array $args, string $exceptionMessage): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage($exceptionMessage);
call_user_func_array(['Webmozart\Assert\Assert', 'isAOf'], $args);
}
public function testResourceOfTypeCustomMessage(): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage('I want a resource of type curl. Got: NULL');
Assert::resource(null, 'curl', 'I want a resource of type %2$s. Got: %s');
}
public function testEnumAssertionErrorMessage(): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage('Expected null. Got: Webmozart\Assert\Tests\DummyEnum::CaseName');
Assert::null(DummyEnum::CaseName, 'Expected null. Got: %s');
}
#[DataProvider('getMethodsThatUseOtherMethods')]
public function testMessageIsPassedToInternalCalls(string $method, array $args, string $exceptionMessage): void
{
$this->expectException('\InvalidArgumentException');
$this->expectExceptionMessage($exceptionMessage);
call_user_func_array(['Webmozart\Assert\Assert', $method], $args);
}
public static function getMethodsThatUseOtherMethods(): array
{
return [
[
'method' => 'positiveInteger',
'args' => ['not-integer', 'Value must be a positive integer. Got: %s'],
'exceptionMessage' => 'Value must be a positive integer. Got: string',
],
[
'method' => 'notNegativeInteger',
'args' => ['not-integer', 'Value must be a non-negative integer. Got: %s'],
'exceptionMessage' => 'Value must be a non-negative integer. Got: string',
],
[
'method' => 'negativeInteger',
'args' => ['not-integer', 'Value must be a negative integer. Got: %s'],
'exceptionMessage' => 'Value must be a negative integer. Got: string',
],
[
'method' => 'ip',
'args' => [127001, 'Value must be a valid IP. Got: %s'],
'exceptionMessage' => 'Value must be a valid IP. Got: integer',
],
[
'method' => 'ipv4',
'args' => [127001, 'Value must be a valid IPv4. Got: %s'],
'exceptionMessage' => 'Value must be a valid IPv4. Got: integer',
],
[
'method' => 'ipv6',
'args' => [127001, 'Value must be a valid IPv6. Got: %s'],
'exceptionMessage' => 'Value must be a valid IPv6. Got: integer',
],
[
'method' => 'email',
'args' => [111111, 'Value must be a valid email. Got: %s'],
'exceptionMessage' => 'Value must be a valid email. Got: integer',
],
[
'method' => 'unicodeLetters',
'args' => [111, 'Value must be a string with valid unicode characters. Got: %s'],
'exceptionMessage' => 'Value must be a string with valid unicode characters. Got: integer',
],
[
'method' => 'alpha',
'args' => [111, 'Value must be a string with only alphabetic characters. Got: %s'],
'exceptionMessage' => 'Value must be a string with only alphabetic characters. Got: integer',
],
[
'method' => 'digits',
'args' => [111, 'Value must be a string with only digits. Got: %s'],
'exceptionMessage' => 'Value must be a string with only digits. Got: integer',
],
[
'method' => 'alnum',
'args' => [111, 'Value must be a string with only alphanumeric characters. Got: %s'],
'exceptionMessage' => 'Value must be a string with only alphanumeric characters. Got: integer',
],
[
'method' => 'lower',
'args' => [111, 'Value must be a string with only lowercase characters. Got: %s'],
'exceptionMessage' => 'Value must be a string with only lowercase characters. Got: integer',
],
[
'method' => 'upper',
'args' => [111, 'Value must be a string with only uppercase characters. Got: %s'],
'exceptionMessage' => 'Value must be a string with only uppercase characters. Got: integer',
],
[
'method' => 'keyExists',
'args' => [111, 'test', 'Value must be an array with key test. Got: %s'],
'exceptionMessage' => 'Value must be an array with key test. Got: integer',
],
[
'method' => 'keyNotExists',
'args' => [111, 'test', 'Value must be an array without key test. Got: %s'],
'exceptionMessage' => 'Value must be an array without key test. Got: integer',
],
[
'method' => 'isInstanceOf',
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'notInstanceOf',
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'isInstanceOfAny',
'args' => [111, ['stdClass'], 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'isNotA',
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'isAnyOf',
'args' => [111, ['stdClass'], 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
];
}
}
/**
* @ignore
*/
class ToStringClass
{
/**
* @var string
*/
private $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function __toString(): string
{
return $this->value;
}
}