Passed
Push — master ( aae6d5...8ff6a9 )
by Alexander
01:59
created

StringHelperTest::testTruncateMiddle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Yiisoft\Strings\Tests;
4
5
use Yiisoft\Strings\StringHelper;
6
use PHPUnit\Framework\TestCase;
7
8
final class StringHelperTest extends TestCase
9
{
10
    public function testStrlen(): void
11
    {
12
        $this->assertEquals(4, StringHelper::byteLength('this'));
13
        $this->assertEquals(6, StringHelper::byteLength('это'));
14
    }
15
16
    public function testSubstr(): void
17
    {
18
        $this->assertEquals('th', StringHelper::byteSubstr('this', 0, 2));
19
        $this->assertEquals('э', StringHelper::byteSubstr('это', 0, 2));
20
21
        $this->assertEquals('abcdef', StringHelper::byteSubstr('abcdef', 0));
22
        $this->assertEquals('abcdef', StringHelper::byteSubstr('abcdef', 0, null));
23
24
        $this->assertEquals('de', StringHelper::byteSubstr('abcdef', 3, 2));
25
        $this->assertEquals('def', StringHelper::byteSubstr('abcdef', 3));
26
        $this->assertEquals('def', StringHelper::byteSubstr('abcdef', 3, null));
27
28
        $this->assertEquals('cd', StringHelper::byteSubstr('abcdef', -4, 2));
29
        $this->assertEquals('cdef', StringHelper::byteSubstr('abcdef', -4));
30
        $this->assertEquals('cdef', StringHelper::byteSubstr('abcdef', -4, null));
31
32
        $this->assertEquals('', StringHelper::byteSubstr('abcdef', 4, 0));
33
        $this->assertEquals('', StringHelper::byteSubstr('abcdef', -4, 0));
34
35
        $this->assertEquals('это', StringHelper::byteSubstr('это', 0));
36
        $this->assertEquals('это', StringHelper::byteSubstr('это', 0, null));
37
38
        $this->assertEquals('т', StringHelper::byteSubstr('это', 2, 2));
39
        $this->assertEquals('то', StringHelper::byteSubstr('это', 2));
40
        $this->assertEquals('то', StringHelper::byteSubstr('это', 2, null));
41
42
        $this->assertEquals('т', StringHelper::byteSubstr('это', -4, 2));
43
        $this->assertEquals('то', StringHelper::byteSubstr('это', -4));
44
        $this->assertEquals('то', StringHelper::byteSubstr('это', -4, null));
45
46
        $this->assertEquals('', StringHelper::byteSubstr('это', 4, 0));
47
        $this->assertEquals('', StringHelper::byteSubstr('это', -4, 0));
48
    }
49
50
    public function testBasename(): void
51
    {
52
        $this->assertEquals('', StringHelper::basename(''));
53
54
        $this->assertEquals('file', StringHelper::basename('file'));
55
        $this->assertEquals('file.test', StringHelper::basename('file.test', '.test2'));
56
        $this->assertEquals('file', StringHelper::basename('file.test', '.test'));
57
58
        $this->assertEquals('file', StringHelper::basename('/file'));
59
        $this->assertEquals('file.test', StringHelper::basename('/file.test', '.test2'));
60
        $this->assertEquals('file', StringHelper::basename('/file.test', '.test'));
61
62
        $this->assertEquals('file', StringHelper::basename('/path/to/file'));
63
        $this->assertEquals('file.test', StringHelper::basename('/path/to/file.test', '.test2'));
64
        $this->assertEquals('file', StringHelper::basename('/path/to/file.test', '.test'));
65
66
        $this->assertEquals('file', StringHelper::basename('\file'));
67
        $this->assertEquals('file.test', StringHelper::basename('\file.test', '.test2'));
68
        $this->assertEquals('file', StringHelper::basename('\file.test', '.test'));
69
70
        $this->assertEquals('file', StringHelper::basename('C:\file'));
71
        $this->assertEquals('file.test', StringHelper::basename('C:\file.test', '.test2'));
72
        $this->assertEquals('file', StringHelper::basename('C:\file.test', '.test'));
73
74
        $this->assertEquals('file', StringHelper::basename('C:\path\to\file'));
75
        $this->assertEquals('file.test', StringHelper::basename('C:\path\to\file.test', '.test2'));
76
        $this->assertEquals('file', StringHelper::basename('C:\path\to\file.test', '.test'));
77
78
        // mixed paths
79
        $this->assertEquals('file.test', StringHelper::basename('/path\to/file.test'));
80
        $this->assertEquals('file.test', StringHelper::basename('/path/to\file.test'));
81
        $this->assertEquals('file.test', StringHelper::basename('\path/to\file.test'));
82
83
        // \ and / in suffix
84
        $this->assertEquals('file', StringHelper::basename('/path/to/filete/st', 'te/st'));
85
        $this->assertEquals('st', StringHelper::basename('/path/to/filete/st', 'te\st'));
86
        $this->assertEquals('file', StringHelper::basename('/path/to/filete\st', 'te\st'));
87
        $this->assertEquals('st', StringHelper::basename('/path/to/filete\st', 'te/st'));
88
89
        // http://www.php.net/manual/en/function.basename.php#72254
90
        $this->assertEquals('foo', StringHelper::basename('/bar/foo/'));
91
        $this->assertEquals('foo', StringHelper::basename('\\bar\\foo\\'));
92
    }
93
94
    public function testTruncate(): void
95
    {
96
        $this->assertEquals('привет, я multibyte…', StringHelper::truncateCharacters('привет, я multibyte строка!', 20));
97
        $this->assertEquals('Не трогаем строку', StringHelper::truncateCharacters('Не трогаем строку', 20));
98
        $this->assertEquals('исполь!!!', StringHelper::truncateCharacters('используем восклицательные знаки', 6, '!!!'));
99
    }
100
101
    public function testTruncateWords(): void
102
    {
103
        $this->assertEquals('это тестовая multibyte строка', StringHelper::truncateWords('это тестовая multibyte строка', 5));
104
        $this->assertEquals('это тестовая multibyte…', StringHelper::truncateWords('это тестовая multibyte строка', 3));
105
        $this->assertEquals('это тестовая multibyte!!!', StringHelper::truncateWords('это тестовая multibyte строка', 3, '!!!'));
106
        $this->assertEquals('это строка с          неожиданными…', StringHelper::truncateWords('это строка с          неожиданными пробелами', 4));
107
    }
108
109
    /**
110
     * @dataProvider providerStartsWith
111
     * @param bool $result
112
     * @param string $string
113
     * @param string|null $with
114
     */
115
    public function testStartsWith(bool $result, string $string, ?string $with): void
116
    {
117
        // case sensitive version check
118
        $this->assertSame($result, StringHelper::startsWith($string, $with));
119
        // case insensitive version check
120
        $this->assertSame($result, StringHelper::startsWith($string, $with, false));
121
    }
122
123
    /**
124
     * Rules that should work the same for case-sensitive and case-insensitive `startsWith()`.
125
     */
126
    public function providerStartsWith(): array
127
    {
128
        return [
129
            // positive check
130
            [true, '', ''],
131
            [true, '', null],
132
            [true, 'string', ''],
133
            [true, ' string', ' '],
134
            [true, 'abc', 'abc'],
135
            [true, 'Bürger', 'Bürger'],
136
            [true, '我Я multibyte', '我Я'],
137
            [true, 'Qנטשופ צרכנות', 'Qנ'],
138
            [true, 'ไทย.idn.icann.org', 'ไ'],
139
            [true, '!?+', "\x21\x3F"],
140
            [true, "\x21?+", '!?'],
141
            // false-positive check
142
            [false, '', ' '],
143
            [false, ' ', '  '],
144
            [false, 'Abc', 'Abcde'],
145
            [false, 'abc', 'abe'],
146
            [false, 'abc', 'b'],
147
            [false, 'abc', 'c'],
148
        ];
149
    }
150
151
    public function testStartsWithCaseSensitive(): void
152
    {
153
        $this->assertFalse(StringHelper::startsWith('Abc', 'a'));
154
        $this->assertFalse(StringHelper::startsWith('üЯ multibyte', 'Üя multibyte'));
155
    }
156
157
    public function testStartsWithCaseInsensitive(): void
158
    {
159
        $this->assertTrue(StringHelper::startsWith('sTrInG', 'StRiNg', false));
160
        $this->assertTrue(StringHelper::startsWith('CaSe', 'cAs', false));
161
        $this->assertTrue(StringHelper::startsWith('HTTP://BÜrger.DE/', 'http://bürger.de', false));
162
        $this->assertTrue(StringHelper::startsWith('üЯйΨB', 'ÜяЙΨ', false));
163
    }
164
165
    /**
166
     * @dataProvider providerEndsWith
167
     * @param bool $result
168
     * @param string $string
169
     * @param string|null $with
170
     */
171
    public function testEndsWith(bool $result, string $string, ?string $with): void
172
    {
173
        // case sensitive version check
174
        $this->assertSame($result, StringHelper::endsWith($string, $with));
175
        // case insensitive version check
176
        $this->assertSame($result, StringHelper::endsWith($string, $with, false));
177
    }
178
179
    /**
180
     * Rules that should work the same for case-sensitive and case-insensitive `endsWith()`.
181
     */
182
    public function providerEndsWith(): array
183
    {
184
        return [
185
            // positive check
186
            [true, '', ''],
187
            [true, '', null],
188
            [true, 'string', ''],
189
            [true, 'string ', ' '],
190
            [true, 'string', 'g'],
191
            [true, 'abc', 'abc'],
192
            [true, 'Bürger', 'Bürger'],
193
            [true, 'Я multibyte строка我!', ' строка我!'],
194
            [true, '+!?', "\x21\x3F"],
195
            [true, "+\x21?", "!\x3F"],
196
            [true, 'נטשופ צרכנות', 'ת'],
197
            // false-positive check
198
            [false, '', ' '],
199
            [false, ' ', '  '],
200
            [false, 'aaa', 'aaaa'],
201
            [false, 'abc', 'abe'],
202
            [false, 'abc', 'a'],
203
            [false, 'abc', 'b'],
204
        ];
205
    }
206
207
    public function testEndsWithCaseSensitive(): void
208
    {
209
        $this->assertFalse(StringHelper::endsWith('string', 'G'));
210
        $this->assertFalse(StringHelper::endsWith('multibyte строка', 'А'));
211
    }
212
213
    public function testEndsWithCaseInsensitive(): void
214
    {
215
        $this->assertTrue(StringHelper::endsWith('sTrInG', 'StRiNg', false));
216
        $this->assertTrue(StringHelper::endsWith('string', 'nG', false));
217
        $this->assertTrue(StringHelper::endsWith('BüЯйΨ', 'ÜяЙΨ', false));
218
    }
219
220
    public function testExplode(): void
221
    {
222
        $this->assertEquals(['It', 'is', 'a first', 'test'], StringHelper::explode('It, is, a first, test'));
223
        $this->assertEquals(['It', 'is', 'a test with trimmed digits', '0', '1', '2'], StringHelper::explode('It, is, a test with trimmed digits, 0, 1, 2', ',', true, true));
224
        $this->assertEquals(['It', 'is', 'a second', 'test'], StringHelper::explode('It+ is+ a second+ test', '+'));
225
        $this->assertEquals(['Save', '', '', 'empty trimmed string'], StringHelper::explode('Save, ,, empty trimmed string', ','));
226
        $this->assertEquals(['44', '512'], StringHelper::explode('0 0 440 512', ' ', '0', true));
227
        $this->assertEquals(['Здесь', 'multibyte', 'строка'], StringHelper::explode('Здесь我 multibyte我 строка', '我'));
228
        $this->assertEquals(['Disable', '  trim  ', 'here but ignore empty'], StringHelper::explode('Disable,  trim  ,,,here but ignore empty', ',', false, true));
229
        $this->assertEquals(['It/', ' is?', ' a', ' test with rtrim'], StringHelper::explode('It/, is?, a , test with rtrim', ',', 'rtrim'));
230
        $this->assertEquals(['It', ' is', ' a ', ' test with closure'], StringHelper::explode('It/, is?, a , test with closure', ',', static function ($value) {
231
            return trim($value, '/?');
232
        }));
233
    }
234
235
    public function testWordCount(): void
236
    {
237
        $this->assertEquals(3, StringHelper::countWords('china 中国 ㄍㄐㄋㄎㄌ'));
238
        $this->assertEquals(4, StringHelper::countWords('и много тут слов?'));
239
        $this->assertEquals(4, StringHelper::countWords("и\rмного\r\nтут\nслов?"));
240
        $this->assertEquals(1, StringHelper::countWords('крем-брюле'));
241
        $this->assertEquals(1, StringHelper::countWords(' слово '));
242
    }
243
244
    /**
245
     * @dataProvider base64UrlEncodedStringsProvider
246
     * @param string $input
247
     * @param string $base64UrlEncoded
248
     */
249
    public function testBase64UrlEncode(string $input, string $base64UrlEncoded): void
250
    {
251
        $encoded = StringHelper::base64UrlEncode($input);
252
        $this->assertEquals($base64UrlEncoded, $encoded);
253
    }
254
255
    /**
256
     * @dataProvider base64UrlEncodedStringsProvider
257
     * @param $output
258
     * @param $base64UrlEncoded
259
     */
260
    public function testBase64UrlDecode($output, $base64UrlEncoded): void
261
    {
262
        $decoded = StringHelper::base64UrlDecode($base64UrlEncoded);
263
        $this->assertEquals($output, $decoded);
264
    }
265
266
    public function base64UrlEncodedStringsProvider(): array
267
    {
268
        return [
269
            'Regular string' => ['This is an encoded string', 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw=='],
270
            '? and _ characters' => ['subjects?_d=1', 'c3ViamVjdHM_X2Q9MQ=='],
271
            '> character' => ['subjects>_d=1', 'c3ViamVjdHM-X2Q9MQ=='],
272
            'Unicode' => ['Это закодированная строка', '0K3RgtC-INC30LDQutC-0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw'],
273
        ];
274
    }
275
276
    /**
277
     * Data provider for [[testMatchWildcard()]]
278
     * @return array test data.
279
     */
280
    public function dataProviderMatchWildcard(): array
281
    {
282
        return [
283
            // *
284
            ['*', 'any', true],
285
            ['*', '', true],
286
            ['begin*end', 'begin-middle-end', true],
287
            ['begin*end', 'beginend', true],
288
            ['begin*end', 'begin-d', false],
289
            ['*end', 'beginend', true],
290
            ['*end', 'begin', false],
291
            ['begin*', 'begin-end', true],
292
            ['begin*', 'end', false],
293
            ['begin*', 'before-begin', false],
294
            // ?
295
            ['begin?end', 'begin1end', true],
296
            ['begin?end', 'beginend', false],
297
            ['begin??end', 'begin12end', true],
298
            ['begin??end', 'begin1end', false],
299
            // []
300
            ['gr[ae]y', 'gray', true],
301
            ['gr[ae]y', 'grey', true],
302
            ['gr[ae]y', 'groy', false],
303
            ['a[2-8]', 'a1', false],
304
            ['a[2-8]', 'a3', true],
305
            ['[][!]', ']', true],
306
            ['[-1]', '-', true],
307
            // [!]
308
            ['gr[!ae]y', 'gray', false],
309
            ['gr[!ae]y', 'grey', false],
310
            ['gr[!ae]y', 'groy', true],
311
            ['a[!2-8]', 'a1', true],
312
            ['a[!2-8]', 'a3', false],
313
            // -
314
            ['a-z', 'a-z', true],
315
            ['a-z', 'a-c', false],
316
            // slashes
317
            ['begin/*/end', 'begin/middle/end', true],
318
            ['begin/*/end', 'begin/two/steps/end', true],
319
            ['begin/*/end', 'begin/end', false],
320
            ['begin\\\\*\\\\end', 'begin\middle\end', true],
321
            ['begin\\\\*\\\\end', 'begin\two\steps\end', true],
322
            ['begin\\\\*\\\\end', 'begin\end', false],
323
            // dots
324
            ['begin.*.end', 'begin.middle.end', true],
325
            ['begin.*.end', 'begin.two.steps.end', true],
326
            ['begin.*.end', 'begin.end', false],
327
            // case
328
            ['begin*end', 'BEGIN-middle-END', false],
329
            ['begin*end', 'BEGIN-middle-END', true, ['caseSensitive' => false]],
330
            // file path
331
            ['begin/*/end', 'begin/middle/end', true, ['filePath' => true]],
332
            ['begin/*/end', 'begin/two/steps/end', false, ['filePath' => true]],
333
            ['begin\\\\*\\\\end', 'begin\middle\end', true, ['filePath' => true]],
334
            ['begin\\\\*\\\\end', 'begin\two\steps\end', false, ['filePath' => true]],
335
            ['*', 'any', true, ['filePath' => true]],
336
            ['*', 'any/path', false, ['filePath' => true]],
337
            ['[.-0]', 'any/path', false, ['filePath' => true]],
338
            ['*', '.dotenv', true, ['filePath' => true]],
339
            // escaping
340
            ['\*\?', '*?', true],
341
            ['\*\?', 'zz', false],
342
            ['begin\*\end', 'begin\middle\end', true, ['escape' => false]],
343
            ['begin\*\end', 'begin\two\steps\end', true, ['escape' => false]],
344
            ['begin\*\end', 'begin\end', false, ['escape' => false]],
345
            ['begin\*\end', 'begin\middle\end', true, ['filePath' => true, 'escape' => false]],
346
            ['begin\*\end', 'begin\two\steps\end', false, ['filePath' => true, 'escape' => false]],
347
        ];
348
    }
349
350
    /**
351
     * @dataProvider dataProviderMatchWildcard
352
     *
353
     * @param string $pattern
354
     * @param string $string
355
     * @param bool $expectedResult
356
     * @param array $options
357
     */
358
    public function testMatchWildcard(string $pattern, string $string, bool $expectedResult, array $options = []): void
359
    {
360
        $this->assertSame($expectedResult, StringHelper::matchWildcard($pattern, $string, $options));
361
    }
362
363
    public function dataProviderUcfirst(): array
364
    {
365
        return [
366
            ['foo', 'Foo'],
367
            ['foo bar', 'Foo bar'],
368
            ['👍🏻 foo bar', '👍🏻 foo bar'],
369
            ['', ''],
370
            ['здесь我 multibyte我 строка', 'Здесь我 multibyte我 строка'],
371
        ];
372
    }
373
374
    /**
375
     * @param string $string
376
     * @param string $expectedResult
377
     * @dataProvider dataProviderUcfirst
378
     */
379
    public function testUcfirst(string $string, string $expectedResult): void
380
    {
381
        $this->assertSame($expectedResult, StringHelper::ucfirst($string));
382
    }
383
384
    public function dataProviderUcwords(): array
385
    {
386
        return [
387
            'Single word' => ['foo', 'Foo'],
388
            'Multiple words' => ['foo bar', 'Foo Bar'],
389
            'Unicode smileys' => ['👍🏻 foo bar', '👍🏻 Foo Bar'],
390
            'Empty' => ['', ''],
391
            'Unciode' => ['здесь我 multibyte我 строка', 'Здесь我 Multibyte我 Строка'],
392
        ];
393
    }
394
395
    /**
396
     * @param string $string
397
     * @param string $expectedResult
398
     * @dataProvider dataProviderUcwords
399
     */
400
    public function testUcwords(string $string, string $expectedResult): void
401
    {
402
        $this->assertSame($expectedResult, StringHelper::ucwords($string));
403
    }
404
405
    public function testTruncateBegin(): void
406
    {
407
        $this->assertSame('sms sent to …456', 'sms sent to ' . StringHelper::truncateBegin('123456', 3));
408
        $this->assertSame('sms sent to *56', 'sms sent to ' . StringHelper::truncateBegin('123456', 4, '*'));
409
    }
410
411
    public function testTruncateMiddle(): void
412
    {
413
        $this->assertSame('Hell...er 2', StringHelper::truncateMiddle('Hello world number 2', 8));
414
        $this->assertSame('Hell*er 2', StringHelper::truncateMiddle('Hello world number 2', 8, '*'));
415
    }
416
}
417