diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php new file mode 100644 index 0000000000000..baff67eaaf38b --- /dev/null +++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php @@ -0,0 +1,732 @@ +assertSame('Symfony is a PHP framework!', (string) $bytes); + $this->assertSame(27, $bytes->length()); + $this->assertFalse($bytes->isEmpty()); + } + + public function testCreateFromEmptyString() + { + $instance = static::createFromString(''); + + $this->assertSame('', (string) $instance); + $this->assertSame(0, $instance->length()); + $this->assertTrue($instance->isEmpty()); + } + + /** + * @dataProvider provideLength + */ + public function testLength(int $length, string $string) + { + $instance = static::createFromString($string); + + $this->assertSame($length, $instance->length()); + } + + public static function provideLength(): array + { + return [ + [1, 'a'], + [2, 'is'], + [3, 'PHP'], + [4, 'Java'], + [7, 'Symfony'], + [10, 'pineapples'], + [22, 'Symfony is super cool!'], + ]; + } + + /** + * @dataProvider provideIndexOf + */ + public function testIndexOf(?int $result, string $string, string $needle, int $offset) + { + $instance = static::createFromString($string); + + $this->assertSame($result, $instance->indexOf($needle, $offset)); + } + + public static function provideIndexOf(): array + { + return [ + [null, 'abc', '', 0], + [null, 'ABC', '', 0], + [null, 'abc', 'd', 0], + [null, 'abc', 'a', 3], + [null, 'ABC', 'c', 0], + [null, 'ABC', 'c', 2], + [null, 'abc', 'a', -1], + [null, '123abc', 'B', -3], + [null, '123abc', 'b', 6], + [0, 'abc', 'a', 0], + [1, 'abc', 'b', 1], + [2, 'abc', 'c', 1], + [4, '123abc', 'b', -3], + ]; + } + + /** + * @dataProvider provideIndexOfIgnoreCase + */ + public function testIndexOfIgnoreCase(?int $result, string $string, string $needle, int $offset) + { + $instance = static::createFromString($string); + + $this->assertSame($result, $instance->ignoreCase()->indexOf($needle, $offset)); + } + + public static function provideIndexOfIgnoreCase(): array + { + return [ + [null, 'ABC', '', 0], + [null, 'ABC', '', 0], + [null, 'abc', 'a', 3], + [null, 'abc', 'A', 3], + [null, 'abc', 'a', -1], + [null, 'abc', 'A', -1], + [null, '123abc', 'B', 6], + [0, 'ABC', 'a', 0], + [0, 'ABC', 'A', 0], + [1, 'ABC', 'b', 0], + [1, 'ABC', 'b', 1], + [2, 'ABC', 'c', 0], + [2, 'ABC', 'c', 2], + [4, '123abc', 'B', -3], + ]; + } + + /** + * @dataProvider provideIndexOfLast + */ + public function testIndexOfLast(?int $result, string $string, string $needle, int $offset) + { + $instance = static::createFromString($string); + + $this->assertSame($result, $instance->indexOfLast($needle, $offset)); + } + + public static function provideIndexOfLast(): array + { + return [ + [null, 'abc', '', 0], + [null, 'abc', '', -2], + [null, 'elegant', 'z', -1], + [5, 'DEJAAAA', 'A', -2], + [74, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'i', 0], + [19, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'i', -40], + [6, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'ipsum', 0], + [57, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'amet', 0], + [57, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'amet', -10], + [22, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'amet', -30], + ]; + } + + /** + * @dataProvider provideIndexOfLastIgnoreCase + */ + public function testIndexOfLastIgnoreCase(?int $result, string $string, string $needle, int $offset) + { + $instance = static::createFromString($string); + + $this->assertSame($result, $instance->ignoreCase()->indexOfLast($needle, $offset)); + } + + public static function provideIndexOfLastIgnoreCase(): array + { + return [ + [null, 'abc', '', 0], + [null, 'abc', '', -2], + [null, 'elegant', 'z', -1], + [1, 'abc', 'b', 0], + [1, 'abc', 'b', -1], + [2, 'abcdefgh', 'c', -1], + [2, 'abcdefgh', 'C', -1], + [5, 'dejaaaa', 'A', -2], + [5, 'DEJAAAA', 'a', -2], + [74, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'I', 0], + [19, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'I', -40], + [6, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'IPSUM', 0], + [57, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'AmeT', 0], + [57, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'aMEt', -10], + [22, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, amet sagittis felis.', 'AMET', -30], + ]; + } + + /** + * @dataProvider provideSplit + */ + public function testSplit(string $string, string $delimiter, array $chunks, ?int $limit) + { + $this->assertEquals($chunks, static::createFromString($string)->split($delimiter, $limit)); + } + + public static function provideSplit(): array + { + return [ + [ + 'hello world', + ' ', + [ + static::createFromString('hello'), + static::createFromString('world'), + ], + null, + ], + [ + 'radar', + 'd', + [ + static::createFromString('ra'), + static::createFromString('ar'), + ], + 2, + ], + [ + 'foo,bar,baz,qux,kix', + ',', + [ + static::createFromString('foo'), + static::createFromString('bar'), + static::createFromString('baz'), + static::createFromString('qux'), + static::createFromString('kix'), + ], + null, + ], + [ + 'foo,bar,baz,qux,kix', + ',', + [ + static::createFromString('foo,bar,baz,qux,kix'), + ], + 1, + ], + [ + 'foo,bar,baz,qux,kix', + ',', + [ + static::createFromString('foo'), + static::createFromString('bar'), + static::createFromString('baz,qux,kix'), + ], + 3, + ], + [ + 'Quisque viverra tincidunt elit. Vestibulum convallis dui nec lacis suscipit cursus.', + 'is', + [ + static::createFromString('Qu'), + static::createFromString('que viverra tincidunt elit. Vestibulum convall'), + static::createFromString(' dui nec lac'), + static::createFromString(' suscipit cursus.'), + ], + null, + ], + ]; + } + + /** + * @dataProvider provideInvalidChunkLength + */ + public function testInvalidChunkLength(int $length) + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('foo|bar|baz')->chunk($length); + } + + public static function provideInvalidChunkLength(): array + { + return [ + [-2], + [-1], + [0], + ]; + } + + /** + * @dataProvider provideChunk + */ + public function testChunk(string $string, array $chunks, int $length) + { + $this->assertEquals($chunks, static::createFromString($string)->chunk($length)); + } + + public static function provideChunk() + { + return [ + [ + '', + [], + 1, + ], + [ + 'hello', + [ + static::createFromString('h'), + static::createFromString('e'), + static::createFromString('l'), + static::createFromString('l'), + static::createFromString('o'), + ], + 1, + ], + [ + 'hello you!', + [ + static::createFromString('h'), + static::createFromString('e'), + static::createFromString('l'), + static::createFromString('l'), + static::createFromString('o'), + static::createFromString(' '), + static::createFromString('y'), + static::createFromString('o'), + static::createFromString('u'), + static::createFromString('!'), + ], + 1, + ], + [ + 'hell', + [ + static::createFromString('h'), + static::createFromString('e'), + static::createFromString('l'), + static::createFromString('l'), + ], + 1, + ], + [ + 'hell', + [ + static::createFromString('he'), + static::createFromString('ll'), + ], + 2, + ], + [ + str_repeat('-', 65537), + [ + static::createFromString(str_repeat('-', 65536)), + static::createFromString('-'), + ], + 65536, + ], + ]; + } + + /** + * @dataProvider provideLower + */ + public function testLower(string $expected, string $origin) + { + $instance = static::createFromString($origin)->lower(); + + $this->assertNotSame(static::createFromString($origin), $instance); + $this->assertEquals(static::createFromString($expected), $instance); + $this->assertSame($expected, (string) $instance); + } + + public static function provideLower() + { + return [ + ['hello world', 'hello world'], + ['hello world', 'HELLO WORLD'], + ['hello world', 'Hello World'], + ['symfony', 'symfony'], + ['symfony', 'Symfony'], + ['symfony', 'sYmFOny'], + ]; + } + + /** + * @dataProvider provideUpper + */ + public function testUpper(string $expected, string $origin) + { + $instance = static::createFromString($origin)->upper(); + + $this->assertNotSame(static::createFromString($origin), $instance); + $this->assertEquals(static::createFromString($expected), $instance); + $this->assertSame($expected, (string) $instance); + } + + public static function provideUpper() + { + return [ + ['HELLO WORLD', 'hello world'], + ['HELLO WORLD', 'HELLO WORLD'], + ['HELLO WORLD', 'Hello World'], + ['SYMFONY', 'symfony'], + ['SYMFONY', 'Symfony'], + ['SYMFONY', 'sYmFOny'], + ]; + } + + /** + * @dataProvider provideTitle + */ + public function testTitle(string $expected, string $origin, bool $allWords) + { + $this->assertEquals( + static::createFromString($expected), + static::createFromString($origin)->title($allWords) + ); + } + + public static function provideTitle() + { + return [ + ['Hello world', 'hello world', false], + ['Hello World', 'hello world', true], + ['HELLO WORLD', 'HELLO WORLD', false], + ['HELLO WORLD', 'HELLO WORLD', true], + ['HELLO wORLD', 'hELLO wORLD', false], + ['HELLO WORLD', 'hELLO wORLD', true], + ['Symfony', 'symfony', false], + ['Symfony', 'Symfony', false], + ['SYmFOny', 'sYmFOny', false], + ]; + } + + /** + * @dataProvider provideSlice + */ + public function testSlice(string $expected, string $origin, int $start, int $length = null) + { + $this->assertEquals( + static::createFromString($expected), + static::createFromString($origin)->slice($start, $length) + ); + } + + public static function provideSlice() + { + return [ + ['Symfony', 'Symfony is awesome', 0, 7], + [' ', 'Symfony is awesome', 7, 1], + ['is', 'Symfony is awesome', 8, 2], + [' ', 'Symfony is awesome', 10, 1], + ['awesome', 'Symfony is awesome', 11, 7], + ]; + } + + /** + * @dataProvider provideAppend + */ + public function testAppend(string $expected, array $suffixes) + { + $instance = static::createFromString(''); + foreach ($suffixes as $suffix) { + $instance = $instance->append($suffix); + } + + $this->assertEquals($expected, $instance); + + $instance = static::createFromString('')->append(...$suffixes); + + $this->assertEquals(static::createFromString($expected), $instance); + } + + public static function provideAppend() + { + return [ + [ + 'Symfony', + ['Sym', 'fony'], + ], + [ + 'Hello World!', + ['Hel', 'lo', ' ', 'World', '!'], + ], + ]; + } + + /** + * @dataProvider provideAppend + */ + public function testPrepend(string $expected, array $prefixes) + { + $instance = static::createFromString(''); + foreach (array_reverse($prefixes) as $suffix) { + $instance = $instance->prepend($suffix); + } + + $this->assertEquals(static::createFromString($expected), $instance); + + $instance = static::createFromString('')->prepend(...$prefixes); + + $this->assertEquals(static::createFromString($expected), $instance); + } + + /** + * @dataProvider provideTrim + */ + public function testTrim(string $expected, string $origin, ?string $chars) + { + $result = static::createFromString($origin); + $result = null !== $chars ? $result->trim($chars) : $result->trim(); + + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideTrim() + { + return [ + [ + "Symfony IS GREAT\t!!!", + " Symfony IS GREAT\t!!!\n", + null, + ], + [ + "Symfony IS GREAT\t!!!\n", + " Symfony IS GREAT\t!!!\n", + ' ', + ], + [ + " Symfony IS GREAT\t!!!", + " Symfony IS GREAT\t!!!\n", + "\n", + ], + [ + "Symfony IS GREAT\t", + " Symfony IS GREAT\t!!!\n", + " \n!", + ], + ]; + } + + /** + * @dataProvider provideTrimStart + */ + public function testTrimStart(string $expected, string $origin, ?string $chars) + { + $result = static::createFromString($origin); + $result = null !== $chars ? $result->trimStart($chars) : $result->trimStart(); + + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideTrimStart() + { + return [ + [ + "Symfony is a PHP framework\n", + "\n\tSymfony is a PHP framework\n", + null, + ], + [ + "\tSymfony is a PHP framework\n", + "\n\tSymfony is a PHP framework\n", + "\n", + ], + ]; + } + + /** + * @dataProvider provideTrimEnd + */ + public function testTrimEnd(string $expected, string $origin, ?string $chars) + { + $result = static::createFromString($origin); + $result = null !== $chars ? $result->trimEnd($chars) : $result->trimEnd(); + + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideTrimEnd() + { + return [ + [ + "\n\tSymfony is a PHP framework", + "\n\tSymfony is a PHP framework \n", + null, + ], + [ + "\n\tSymfony is a PHP framework ", + "\n\tSymfony is a PHP framework \n", + "\n", + ], + ]; + } + + /** + * @dataProvider provideBeforeAfter + */ + public function testBeforeAfter(string $expected, string $needle, string $origin, bool $before) + { + $result = static::createFromString($origin); + $result = $before ? $result->before($needle, false) : $result->after($needle, true); + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideBeforeAfter() + { + return [ + ['', '', 'hello world', true], + ['', '', 'hello world', false], + ['', 'w', 'hello World', true], + ['', 'w', 'hello World', false], + ['hello ', 'w', 'hello world', true], + ['world', 'w', 'hello world', false], + ]; + } + + /** + * @dataProvider provideBeforeAfterIgnoreCase + */ + public function testBeforeAfterIgnoreCase(string $expected, string $needle, string $origin, bool $before) + { + $result = static::createFromString($origin)->ignoreCase(); + $result = $before ? $result->before($needle, false) : $result->after($needle, true); + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideBeforeAfterIgnoreCase() + { + return [ + ['', '', 'hello world', true], + ['', '', 'hello world', false], + ['', 'foo', 'hello world', true], + ['', 'foo', 'hello world', false], + ['hello ', 'w', 'hello world', true], + ['world', 'w', 'hello world', false], + ['hello ', 'W', 'hello world', true], + ['world', 'W', 'hello world', false], + ]; + } + + /** + * @dataProvider provideBeforeAfterLast + */ + public function testBeforeAfterLast(string $expected, string $needle, string $origin, bool $before) + { + $result = static::createFromString($origin); + $result = $before ? $result->beforeLast($needle, false) : $result->afterLast($needle, true); + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideBeforeAfterLast() + { + return [ + ['', '', 'hello world', true], + ['', '', 'hello world', false], + ['', 'L', 'hello world', true], + ['', 'L', 'hello world', false], + ['hello wor', 'l', 'hello world', true], + ['ld', 'l', 'hello world', false], + ['hello w', 'o', 'hello world', true], + ['orld', 'o', 'hello world', false], + ]; + } + + /** + * @dataProvider provideBeforeAfterLastIgnoreCase + */ + public function testBeforeAfterLastIgnoreCase(string $expected, string $needle, string $origin, bool $before) + { + $result = static::createFromString($origin)->ignoreCase(); + $result = $before ? $result->beforeLast($needle, false) : $result->afterLast($needle, true); + $this->assertEquals(static::createFromString($expected), $result); + } + + public static function provideBeforeAfterLastIgnoreCase() + { + return [ + ['', '', 'hello world', true], + ['', '', 'hello world', false], + ['', 'FOO', 'hello world', true], + ['', 'FOO', 'hello world', false], + ['hello wor', 'l', 'hello world', true], + ['ld', 'l', 'hello world', false], + ['hello wor', 'L', 'hello world', true], + ['ld', 'L', 'hello world', false], + ['hello w', 'O', 'hello world', true], + ['orld', 'O', 'hello world', false], + ]; + } + + /** + * @dataProvider provideFolded + */ + public function testFolded(string $expected, string $origin) + { + $this->assertEquals( + static::createFromString($expected), + static::createFromString($origin)->folded() + ); + } + + public static function provideFolded() + { + return [ + ['hello', 'HELlo'], + ['world', 'worLd'], + ]; + } + + /** + * @dataProvider provideReplace + */ + public function testReplace(string $expectedString, int $expectedCount, string $origin, string $from, string $to) + { + $origin = static::createFromString($origin); + $result = $origin->replace($from, $to); + + $this->assertEquals(static::createFromString($expectedString), $result); + } + + public static function provideReplace() + { + return [ + ['hello world', 0, 'hello world', '', ''], + ['hello world', 0, 'hello world', '', '_'], + ['helloworld', 1, 'hello world', ' ', ''], + ['hello_world', 1, 'hello world', ' ', '_'], + ['hemmo wormd', 3, 'hello world', 'l', 'm'], + ['hello world', 0, 'hello world', 'L', 'm'], + ]; + } + + /** + * @dataProvider provideReplaceIgnoreCase + */ + public function testReplaceIgnoreCase(string $expectedString, int $expectedCount, string $origin, string $from, string $to) + { + $origin = static::createFromString($origin); + $result = $origin->ignoreCase()->replace($from, $to); + + $this->assertEquals(static::createFromString($expectedString), $result); + } + + public static function provideReplaceIgnoreCase() + { + return [ + ['hello world', 0, 'hello world', '', ''], + ['hello world', 0, 'hello world', '', '_'], + ['helloworld', 1, 'hello world', ' ', ''], + ['hello_world', 1, 'hello world', ' ', '_'], + ['hemmo wormd', 3, 'hello world', 'l', 'm'], + ['heMMo worMd', 3, 'hello world', 'L', 'M'], + ]; + } +} diff --git a/src/Symfony/Component/String/Tests/AbstractUtf8TestCase.php b/src/Symfony/Component/String/Tests/AbstractUtf8TestCase.php new file mode 100644 index 0000000000000..d2216d5a561f6 --- /dev/null +++ b/src/Symfony/Component/String/Tests/AbstractUtf8TestCase.php @@ -0,0 +1,425 @@ +expectException(InvalidArgumentException::class); + + static::createFromString("\xE9"); + } + + public function provideCreateFromCodePoint(): array + { + return [ + ['', []], + ['*', [42]], + ['AZ', [65, 90]], + ['€', [8364]], + ['€', [0x20ac]], + ['Ʃ', [425]], + ['Ʃ', [0x1a9]], + ['☢☎❄', [0x2622, 0x260E, 0x2744]], + ]; + } + + public static function provideLength(): array + { + return [ + [1, 'a'], + [1, 'ß'], + [2, 'is'], + [3, 'PHP'], + [3, '한국어'], + [4, 'Java'], + [7, 'Symfony'], + [10, 'pineapples'], + [22, 'Symfony is super cool!'], + ]; + } + + public static function provideIndexOf(): array + { + return array_merge( + parent::provideIndexOf(), + [ + [1, '한국어', '국', 0], + [1, '한국어', '국', 1], + [null, '한국어', '국', 2], + [8, 'der Straße nach Paris', 'ß', 4], + ] + ); + } + + public static function provideIndexOfIgnoreCase(): array + { + return array_merge( + parent::provideIndexOfIgnoreCase(), + [ + [3, 'DÉJÀ', 'À', 0], + [3, 'DÉJÀ', 'à', 0], + [1, 'DÉJÀ', 'É', 1], + [1, 'DÉJÀ', 'é', 1], + [1, 'aςσb', 'ΣΣ', 0], + [16, 'der Straße nach Paris', 'Paris', 0], + [8, 'der Straße nach Paris', 'ß', 4], + ] + ); + } + + public static function provideIndexOfLast(): array + { + return array_merge( + parent::provideIndexOfLast(), + [ + [null, '한국어', '', 0], + [1, '한국어', '국', 0], + [5, '한국어어어어국국', '어', 0], + // see https://bugs.php.net/bug.php?id=74264 + [15, 'abcdéf12é45abcdéf', 'é', 0], + [8, 'abcdéf12é45abcdéf', 'é', -4], + ] + ); + } + + public static function provideIndexOfLastIgnoreCase(): array + { + return array_merge( + parent::provideIndexOfLastIgnoreCase(), + [ + [null, '한국어', '', 0], + [3, 'DÉJÀ', 'à', 0], + [3, 'DÉJÀ', 'À', 0], + [6, 'DÉJÀÀÀÀ', 'à', 0], + [6, 'DÉJÀÀÀÀ', 'à', 3], + [5, 'DÉJÀÀÀÀ', 'àà', 0], + [2, 'DÉJÀÀÀÀ', 'jà', 0], + [2, 'DÉJÀÀÀÀ', 'jà', -5], + [6, 'DÉJÀÀÀÀ!', 'à', -2], + // see https://bugs.php.net/bug.php?id=74264 + [5, 'DÉJÀÀÀÀ', 'à', -2], + [15, 'abcdéf12é45abcdéf', 'é', 0], + [8, 'abcdéf12é45abcdéf', 'é', -4], + [1, 'aςσb', 'ΣΣ', 0], + ] + ); + } + + public static function provideSplit(): array + { + return array_merge( + parent::provideSplit(), + [ + [ + '會|意|文|字|/|会|意|文|字', + '|', + [ + static::createFromString('會'), + static::createFromString('意'), + static::createFromString('文'), + static::createFromString('字'), + static::createFromString('/'), + static::createFromString('会'), + static::createFromString('意'), + static::createFromString('文'), + static::createFromString('字'), + ], + null, + ], + [ + '會|意|文|字|/|会|意|文|字', + '|', + [ + static::createFromString('會'), + static::createFromString('意'), + static::createFromString('文'), + static::createFromString('字'), + static::createFromString('/|会|意|文|字'), + ], + 5, + ], + ] + ); + } + + public static function provideChunk(): array + { + return array_merge( + parent::provideChunk(), + [ + [ + 'déjà', + [ + static::createFromString('d'), + static::createFromString('é'), + static::createFromString('j'), + static::createFromString('à'), + ], + 1, + ], + [ + 'déjà', + [ + static::createFromString('dé'), + static::createFromString('jà'), + ], + 2, + ], + ] + ); + } + + public function testTrimWithInvalidUtf8CharList() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->trim("\xE9"); + } + + public function testTrimStartWithInvalidUtf8CharList() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->trimStart("\xE9"); + } + + public function testTrimEndWithInvalidUtf8CharList() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->trimEnd("\xE9"); + } + + public static function provideLower(): array + { + return array_merge( + parent::provideLower(), + [ + // French + ['garçon', 'garçon'], + ['garçon', 'GARÇON'], + ["œuvre d'art", "Œuvre d'Art"], + + // Spanish + ['el niño', 'El Niño'], + + // Romanian + ['împărat', 'Împărat'], + + // Random symbols + ['déjà σσς i̇iıi', 'DÉJÀ Σσς İIıi'], + ] + ); + } + + public static function provideUpper(): array + { + return array_merge( + parent::provideUpper(), + [ + // French + ['GARÇON', 'garçon'], + ['GARÇON', 'GARÇON'], + ["ŒUVRE D'ART", "Œuvre d'Art"], + + // Spanish + ['EL NIÑO', 'El Niño'], + + // Romanian + ['ÎMPĂRAT', 'Împărat'], + + // Random symbols + ['DÉJÀ ΣΣΣ İIII', 'Déjà Σσς İIıi'], + ] + ); + } + + public static function provideTitle(): array + { + return array_merge( + parent::provideTitle(), + [ + ['Deja', 'deja', false], + ['Σσς', 'σσς', false], + ['DEJa', 'dEJa', false], + ['ΣσΣ', 'σσΣ', false], + ['Deja Σσς DEJa ΣσΣ', 'deja σσς dEJa σσΣ', true], + ] + ); + } + + public static function provideSlice(): array + { + return array_merge( + parent::provideSlice(), + [ + ['jà', 'déjà', 2, null], + ['jà', 'déjà', 2, null], + ['jà', 'déjà', -2, null], + ['jà', 'déjà', -2, 3], + ['', 'déjà', -1, 0], + ['', 'déjà', 1, -4], + ['j', 'déjà', -2, -1], + ['', 'déjà', -2, -2], + ['', 'déjà', 5, 0], + ['', 'déjà', -5, 0], + ] + ); + } + + public static function provideAppend(): array + { + return array_merge( + parent::provideAppend(), + [ + [ + 'Déjà Σσς', + ['Déjà', ' ', 'Σσς'], + ], + [ + 'Déjà Σσς İIıi', + ['Déjà', ' Σσς', ' İIıi'], + ], + ] + ); + } + + public function testAppendInvalidUtf8String() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->append("\xE9"); + } + + public static function providePrepend(): array + { + return array_merge( + parent::providePrepend(), + [ + [ + 'Σσς Déjà', + ['Déjà', 'Σσς '], + ], + [ + 'İIıi Σσς Déjà', + ['Déjà', 'Σσς ', 'İIıi '], + ], + ] + ); + } + + public function testPrependInvalidUtf8String() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->prepend("\xE9"); + } + + public static function provideBeforeAfter(): array + { + return array_merge( + parent::provideBeforeAfter(), + [ + ['jàdéjà', 'jà', 'déjàdéjà', false], + ['dé', 'jà', 'déjàdéjà', true], + ] + ); + } + + public static function provideBeforeAfterIgnoreCase(): array + { + return array_merge( + parent::provideBeforeAfterIgnoreCase(), + [ + ['jàdéjà', 'JÀ', 'déjàdéjà', false], + ['dé', 'jÀ', 'déjàdéjà', true], + ['éjàdéjà', 'é', 'déjàdéjà', false], + ['d', 'é', 'déjàdéjà', true], + ['', 'Ç', 'déjàdéjà', false], + ['', 'Ç', 'déjàdéjà', true], + ] + ); + } + + public static function provideBeforeAfterLast(): array + { + return array_merge( + parent::provideBeforeAfterLast(), + [ + ['', 'Ç', 'déjàdéjà', false], + ['', 'Ç', 'déjàdéjà', true], + ['éjà', 'é', 'déjàdéjà', false], + ['déjàd', 'é', 'déjàdéjà', true], + ] + ); + } + + public static function provideBeforeAfterLastIgnoreCase(): array + { + return array_merge( + parent::provideBeforeAfterLastIgnoreCase(), + [ + ['', 'Ç', 'déjàdéjà', false], + ['éjà', 'é', 'déjàdéjà', false], + ['éjà', 'É', 'déjàdéjà', false], + ] + ); + } + + public static function provideToFoldedCase(): array + { + return array_merge( + parent::provideToFoldedCase(), + [ + ['déjà', 'DéjÀ'], + ['σσσ', 'Σσς'], + ['iıi̇i', 'Iıİi'], + ] + ); + } + + public static function provideReplace(): array + { + return array_merge( + parent::provideReplace(), + [ + ['ΣσΣ', 1, 'Σσς', 'ς', 'Σ'], + ['漢字はユニコード', 0, '漢字はユニコード', 'foo', 'bar'], + ['漢字ーユニコード', 1, '漢字はユニコード', 'は', 'ー'], + ['This is a jamais-vu situation!', 1, 'This is a déjà-vu situation!', 'déjà', 'jamais'], + ] + ); + } + + public static function provideReplaceIgnoreCase(): array + { + return array_merge( + parent::provideReplaceIgnoreCase(), + [ + // σ and ς are lowercase variants for Σ + ['ΣΣΣ', 3, 'σσσ', 'σ', 'Σ'], + ['ΣΣΣ', 3, 'σσσ', 'ς', 'Σ'], + ['Σσ', 1, 'σσσ', 'σσ', 'Σ'], + ['漢字はユニコード', 0, '漢字はユニコード', 'foo', 'bar'], + ['漢字ーユニコード', 1, '漢字はユニコード', 'は', 'ー'], + ['This is a jamais-vu situation!', 1, 'This is a déjà-vu situation!', 'DÉjÀ', 'jamais'], + ] + ); + } + + public function testReplaceWithInvalidUtf8Pattern() + { + $this->assertEquals('Symfony', static::createFromString('Symfony')->replace("\xE9", 'p')); + } + + public function testReplaceWithInvalidUtf8PatternReplacement() + { + $this->expectException(InvalidArgumentException::class); + + static::createFromString('Symfony')->replace('f', "\xE9"); + } +} diff --git a/src/Symfony/Component/String/Tests/BinaryStringTest.php b/src/Symfony/Component/String/Tests/BinaryStringTest.php new file mode 100644 index 0000000000000..3605b5db2c1b6 --- /dev/null +++ b/src/Symfony/Component/String/Tests/BinaryStringTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Tests; + +use Symfony\Component\String\AbstractString; +use Symfony\Component\String\BinaryString; + +class BinaryStringTest extends AbstractAsciiTestCase +{ + protected static function createFromString(string $string): AbstractString + { + return new BinaryString($string); + } +} diff --git a/src/Symfony/Component/String/Tests/GraphemeStringTest.php b/src/Symfony/Component/String/Tests/GraphemeStringTest.php new file mode 100644 index 0000000000000..18775de077859 --- /dev/null +++ b/src/Symfony/Component/String/Tests/GraphemeStringTest.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Tests; + +use Symfony\Component\String\AbstractString; +use Symfony\Component\String\GraphemeString; + +class GraphemeStringTest extends AbstractUtf8TestCase +{ + protected static function createFromString(string $string): AbstractString + { + return new GraphemeString($string); + } + + public static function provideLength(): array + { + return array_merge( + parent::provideLength(), + [ + // 5 letters + 3 combining marks + [5, 'अनुच्छेद'], + ] + ); + } + + public static function provideSplit(): array + { + return array_merge( + parent::provideSplit(), + [ + [ + 'अ.नु.च्.छे.द', + '.', + [ + static::createFromString('अ'), + static::createFromString('नु'), + static::createFromString('च्'), + static::createFromString('छे'), + static::createFromString('द'), + ], + null, + ], + ] + ); + } + + public static function provideChunk(): array + { + return array_merge( + parent::provideChunk(), + [ + [ + 'अनुच्छेद', + [ + static::createFromString('अ'), + static::createFromString('नु'), + static::createFromString('च्'), + static::createFromString('छे'), + static::createFromString('द'), + ], + 1, + ], + ] + ); + } + + public static function provideLower(): array + { + return array_merge( + parent::provideLower(), + [ + // Hindi + ['अनुच्छेद', 'अनुच्छेद'], + ] + ); + } + + public static function provideUpper(): array + { + return array_merge( + parent::provideUpper(), + [ + // Hindi + ['अनुच्छेद', 'अनुच्छेद'], + ] + ); + } + + public static function provideAppend(): array + { + return array_merge( + parent::provideAppend(), + [ + [ + 'तद्भव देशज', + ['तद्भव', ' ', 'देशज'], + ], + [ + 'तद्भव देशज विदेशी', + ['तद्भव', ' देशज', ' विदेशी'], + ], + ] + ); + } + + public static function providePrepend(): array + { + return array_merge( + parent::providePrepend(), + [ + [ + 'देशज तद्भव', + ['तद्भव', 'देशज '], + ], + [ + 'विदेशी देशज तद्भव', + ['तद्भव', 'देशज ', 'विदेशी '], + ], + ] + ); + } + + public static function provideBeforeAfter(): array + { + return array_merge( + parent::provideBeforeAfter(), + [ + ['द foo अनुच्छेद', 'द', 'अनुच्छेद foo अनुच्छेद', false], + ['अनुच्छे', 'द', 'अनुच्छेद foo अनुच्छेद', true], + ] + ); + } + + public static function provideBeforeAfterIgnoreCase(): array + { + return array_merge( + parent::provideBeforeAfterIgnoreCase(), + [ + ['', 'छेछे', 'दछेच्नुअ', false], + ['', 'छेछे', 'दछेच्नुअ', true], + ['छेच्नुअ', 'छे', 'दछेच्नुअ', false], + ['द', 'छे', 'दछेच्नुअ', true], + ] + ); + } + + public static function provideBeforeAfterLast(): array + { + return array_merge( + parent::provideBeforeAfterLast(), + [ + ['', 'छेछे', 'दछेच्नुअ-दछेच्नु-अदछेच्नु', false], + ['', 'छेछे', 'दछेच्नुअ-दछेच्नु-अदछेच्नु', true], + ['-दछेच्नु', '-द', 'दछेच्नुअ-दछेच्नु-अद-दछेच्नु', false], + ['दछेच्नुअ-दछेच्नु-अद', '-द', 'दछेच्नुअ-दछेच्नु-अद-दछेच्नु', true], + ] + ); + } + + public static function provideBeforeAfterLastIgnoreCase(): array + { + return array_merge( + parent::provideBeforeAfterLastIgnoreCase(), + [ + ['', 'छेछे', 'दछेच्नुअ-दछेच्नु-अदछेच्नु', false], + ['', 'छेछे', 'दछेच्नुअ-दछेच्नु-अदछेच्नु', true], + ['-दछेच्नु', '-द', 'दछेच्नुअ-दछेच्नु-अद-दछेच्नु', false], + ['दछेच्नुअ-दछेच्नु-अद', '-द', 'दछेच्नुअ-दछेच्नु-अद-दछेच्नु', true], + ] + ); + } + + public static function provideReplace(): array + { + return array_merge( + parent::provideReplace(), + [ + ['Das Innenministerium', 1, 'Das Außenministerium', 'Auß', 'Inn'], + ['दछेच्नुद-दछेच्नु-ददछेच्नु', 2, 'दछेच्नुअ-दछेच्नु-अदछेच्नु', 'अ', 'द'], + ] + ); + } + + public static function provideReplaceIgnoreCase(): array + { + return array_merge( + parent::provideReplaceIgnoreCase(), + [ + ['Das Aussenministerium', 1, 'Das Außenministerium', 'auß', 'Auss'], + ['दछेच्नुद-दछेच्नु-ददछेच्नु', 2, 'दछेच्नुअ-दछेच्नु-अदछेच्नु', 'अ', 'द'], + ] + ); + } +} diff --git a/src/Symfony/Component/String/Tests/Utf8StringTest.php b/src/Symfony/Component/String/Tests/Utf8StringTest.php new file mode 100644 index 0000000000000..b5381b44cfab5 --- /dev/null +++ b/src/Symfony/Component/String/Tests/Utf8StringTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\String\Tests; + +use Symfony\Component\String\AbstractString; +use Symfony\Component\String\Utf8String; + +class Utf8StringTest extends AbstractUtf8TestCase +{ + protected static function createFromString(string $string): AbstractString + { + return new Utf8String($string); + } + + public static function provideLength(): array + { + return array_merge( + parent::provideLength(), + [ + // 8 instead of 5 if it were processed as a grapheme cluster + [8, 'अनुच्छेद'], + ] + ); + } +}