Passed
Push — master ( fd1d20...a5d33e )
by Damian
30:15 queued 20:01
created

ConvertTest::providerTestSlashes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 19
nc 1
nop 0
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core\Tests;
4
5
use SilverStripe\Core\Convert;
6
use SilverStripe\Dev\SapphireTest;
7
use SilverStripe\View\Parsers\URLSegmentFilter;
8
use stdClass;
9
use Exception;
10
use InvalidArgumentException;
11
12
/**
13
 * Test various functions on the {@link Convert} class.
14
 */
15
class ConvertTest extends SapphireTest
16
{
17
18
    protected $usesDatabase = false;
19
20
    private $previousLocaleSetting = null;
21
22
    public function setUp()
23
    {
24
        parent::setUp();
25
        // clear the previous locale setting
26
        $this->previousLocaleSetting = null;
27
    }
28
29
    public function tearDown()
30
    {
31
        parent::tearDown();
32
        // If a test sets the locale, reset it on teardown
33
        if ($this->previousLocaleSetting) {
34
            setlocale(LC_CTYPE, $this->previousLocaleSetting);
35
        }
36
    }
37
38
    /**
39
     * Tests {@link Convert::raw2att()}
40
     */
41
    public function testRaw2Att()
42
    {
43
        $val1 = '<input type="text">';
44
        $this->assertEquals(
45
            '&lt;input type=&quot;text&quot;&gt;',
46
            Convert::raw2att($val1),
47
            'Special characters are escaped'
48
        );
49
50
        $val2 = 'This is some normal text.';
51
        $this->assertEquals(
52
            'This is some normal text.',
53
            Convert::raw2att($val2),
54
            'Normal text is not escaped'
55
        );
56
    }
57
58
    /**
59
     * Tests {@link Convert::raw2htmlatt()}
60
     */
61
    public function testRaw2HtmlAtt()
62
    {
63
        $val1 = '<input type="text">';
64
        $this->assertEquals(
65
            '&lt;input type=&quot;text&quot;&gt;',
66
            Convert::raw2htmlatt($val1),
67
            'Special characters are escaped'
68
        );
69
70
        $val2 = 'This is some normal text.';
71
        $this->assertEquals(
72
            'This is some normal text.',
73
            Convert::raw2htmlatt($val2),
74
            'Normal text is not escaped'
75
        );
76
    }
77
78
    /**
79
     * Tests {@link Convert::html2raw()}
80
     */
81
    public function testHtml2raw()
82
    {
83
        $val1 = 'This has a <strong>strong tag</strong>.';
84
        $this->assertEquals(
85
            'This has a *strong tag*.',
86
            Convert::html2raw($val1),
87
            'Strong tags are replaced with asterisks'
88
        );
89
90
        $val1 = 'This has a <b class="test" style="font-weight: bold">b tag with attributes</b>.';
91
        $this->assertEquals(
92
            'This has a *b tag with attributes*.',
93
            Convert::html2raw($val1),
94
            'B tags with attributes are replaced with asterisks'
95
        );
96
97
        $val2 = 'This has a <strong class="test" style="font-weight: bold">strong tag with attributes</STRONG>.';
98
        $this->assertEquals(
99
            'This has a *strong tag with attributes*.',
100
            Convert::html2raw($val2),
101
            'Strong tags with attributes are replaced with asterisks'
102
        );
103
104
        $val3 = '<script type="application/javascript">Some really nasty javascript here</script>';
105
        $this->assertEquals(
106
            '',
107
            Convert::html2raw($val3),
108
            'Script tags are completely removed'
109
        );
110
111
        $val4 = '<style type="text/css">Some really nasty CSS here</style>';
112
        $this->assertEquals(
113
            '',
114
            Convert::html2raw($val4),
115
            'Style tags are completely removed'
116
        );
117
118
        $val5 = "<script type=\"application/javascript\">Some really nasty\nmultiline javascript here</script>";
119
        $this->assertEquals(
120
            '',
121
            Convert::html2raw($val5),
122
            'Multiline script tags are completely removed'
123
        );
124
125
        $val6 = "<style type=\"text/css\">Some really nasty\nmultiline CSS here</style>";
126
        $this->assertEquals(
127
            '',
128
            Convert::html2raw($val6),
129
            'Multiline style tags are completely removed'
130
        );
131
132
        $val7 = '<p>That&#39;s absolutely correct</p>';
133
        $this->assertEquals(
134
            "That's absolutely correct",
135
            Convert::html2raw($val7),
136
            "Single quotes are decoded correctly"
137
        );
138
139
        $val8 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ' . 'incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud ' . 'exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute ' . 'irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla ' . 'pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia ' . 'deserunt mollit anim id est laborum.';
140
        $this->assertEquals($val8, Convert::html2raw($val8), 'Test long text is unwrapped');
141
        $this->assertEquals(
142
            <<<PHP
143
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
144
do eiusmod tempor incididunt ut labore et dolore magna
145
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
146
ullamco laboris nisi ut aliquip ex ea commodo consequat.
147
Duis aute irure dolor in reprehenderit in voluptate velit
148
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
149
occaecat cupidatat non proident, sunt in culpa qui officia
150
deserunt mollit anim id est laborum.
151
PHP
152
            ,
153
            Convert::html2raw($val8, false, 60),
154
            'Test long text is wrapped'
155
        );
156
    }
157
158
    /**
159
     * Tests {@link Convert::raw2xml()}
160
     */
161
    public function testRaw2Xml()
162
    {
163
        $val1 = '<input type="text">';
164
        $this->assertEquals(
165
            '&lt;input type=&quot;text&quot;&gt;',
166
            Convert::raw2xml($val1),
167
            'Special characters are escaped'
168
        );
169
170
        $val2 = 'This is some normal text.';
171
        $this->assertEquals(
172
            'This is some normal text.',
173
            Convert::raw2xml($val2),
174
            'Normal text is not escaped'
175
        );
176
177
        $val3 = "This is test\nNow on a new line.";
178
        $this->assertEquals(
179
            "This is test\nNow on a new line.",
180
            Convert::raw2xml($val3),
181
            'Newlines are retained. They should not be replaced with <br /> as it is not XML valid'
182
        );
183
    }
184
185
    /**
186
     * Tests {@link Convert::raw2htmlid()}
187
     */
188
    public function testRaw2HtmlID()
189
    {
190
        $val1 = 'test test 123';
191
        $this->assertEquals('test_test_123', Convert::raw2htmlid($val1));
192
193
        $val2 = 'test[test][123]';
194
        $this->assertEquals('test_test_123', Convert::raw2htmlid($val2));
195
196
        $val3 = '[test[[test]][123]]';
197
        $this->assertEquals('test_test_123', Convert::raw2htmlid($val3));
198
199
        $val4 = 'A\\Namespaced\\Class';
200
        $this->assertEquals('A_Namespaced_Class', Convert::raw2htmlid($val4));
201
    }
202
203
    /**
204
     * Tests {@link Convert::xml2raw()}
205
     */
206
    public function testXml2Raw()
207
    {
208
        $val1 = '&lt;input type=&quot;text&quot;&gt;';
209
        $this->assertEquals('<input type="text">', Convert::xml2raw($val1), 'Special characters are escaped');
210
211
        $val2 = 'This is some normal text.';
212
        $this->assertEquals('This is some normal text.', Convert::xml2raw($val2), 'Normal text is not escaped');
213
    }
214
215
    /**
216
     * Tests {@link Convert::xml2raw()}
217
     */
218
    public function testArray2JSON()
219
    {
220
        $val = array(
221
         'Joe' => 'Bloggs',
222
         'Tom' => 'Jones',
223
         'My' => array(
224
          'Complicated' => 'Structure'
225
         )
226
        );
227
        $encoded = Convert::array2json($val);
228
        $this->assertEquals(
229
            '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}',
230
            $encoded,
231
            'Array is encoded in JSON'
232
        );
233
    }
234
235
    /**
236
     * Tests {@link Convert::json2array()}
237
     */
238
    public function testJSON2Array()
239
    {
240
        $val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
241
        $decoded = Convert::json2array($val);
242
        $this->assertEquals(3, count($decoded), '3 items in the decoded array');
0 ignored issues
show
Bug introduced by
It seems like $decoded can also be of type boolean; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

242
        $this->assertEquals(3, count(/** @scrutinizer ignore-type */ $decoded), '3 items in the decoded array');
Loading history...
243
        $this->assertContains('Bloggs', $decoded, 'Contains "Bloggs" value in decoded array');
244
        $this->assertContains('Jones', $decoded, 'Contains "Jones" value in decoded array');
245
        $this->assertContains('Structure', $decoded['My']['Complicated']);
246
    }
247
248
    /**
249
     * Tests {@link Convert::testJSON2Obj()}
250
     */
251
    public function testJSON2Obj()
252
    {
253
        $val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
254
        $obj = Convert::json2obj($val);
255
        $this->assertEquals('Bloggs', $obj->Joe);
256
        $this->assertEquals('Jones', $obj->Tom);
257
        $this->assertEquals('Structure', $obj->My->Complicated);
258
    }
259
260
    /**
261
     * Tests {@link Convert::testRaw2URL()}
262
     *
263
     * @todo test toASCII()
264
     */
265
    public function testRaw2URL()
266
    {
267
        URLSegmentFilter::config()->update('default_allow_multibyte', false);
268
        $this->assertEquals('foo', Convert::raw2url('foo'));
269
        $this->assertEquals('foo-and-bar', Convert::raw2url('foo & bar'));
270
        $this->assertEquals('foo-and-bar', Convert::raw2url('foo &amp; bar!'));
271
        $this->assertEquals('foos-bar-2', Convert::raw2url('foo\'s [bar] (2)'));
272
    }
273
274
    /**
275
     * Helper function for comparing characters with significant whitespaces
276
     *
277
     * @param string $expected
278
     * @param string $actual
279
     */
280
    protected function assertEqualsQuoted($expected, $actual)
281
    {
282
        $message = sprintf(
283
            "Expected \"%s\" but given \"%s\"",
284
            addcslashes($expected, "\r\n"),
285
            addcslashes($actual, "\r\n")
286
        );
287
        $this->assertEquals($expected, $actual, $message);
288
    }
289
290
    /**
291
     * Tests {@link Convert::nl2os()}
292
     */
293
    public function testNL2OS()
294
    {
295
296
        foreach (array("\r\n", "\r", "\n") as $nl) {
297
            // Base case: no action
298
            $this->assertEqualsQuoted(
299
                "Base case",
300
                Convert::nl2os("Base case", $nl)
301
            );
302
303
            // Mixed formats
304
            $this->assertEqualsQuoted(
305
                "Test{$nl}Text{$nl}Is{$nl}{$nl}Here{$nl}.",
306
                Convert::nl2os("Test\rText\r\nIs\n\rHere\r\n.", $nl)
307
            );
308
309
            // Test that multiple runs are non-destructive
310
            $expected = "Test{$nl}Text{$nl}Is{$nl}{$nl}Here{$nl}.";
311
            $this->assertEqualsQuoted(
312
                $expected,
313
                Convert::nl2os($expected, $nl)
314
            );
315
316
            // Check repeated sequence behaves correctly
317
            $expected = "{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}";
318
            $input = "\r\r\n\r\r\n\n\n\n\r";
319
            $this->assertEqualsQuoted(
320
                $expected,
321
                Convert::nl2os($input, $nl)
322
            );
323
        }
324
    }
325
326
    /**
327
     * Tests {@link Convert::raw2js()}
328
     */
329
    public function testRaw2JS()
330
    {
331
        // Test attempt to break out of string
332
        $this->assertEquals(
333
            '\\"; window.location=\\"http://www.google.com',
334
            Convert::raw2js('"; window.location="http://www.google.com')
335
        );
336
        $this->assertEquals(
337
            '\\\'; window.location=\\\'http://www.google.com',
338
            Convert::raw2js('\'; window.location=\'http://www.google.com')
339
        );
340
        // Test attempt to close script tag
341
        $this->assertEquals(
342
            '\\"; \\x3c/script\\x3e\\x3ch1\\x3eHa \\x26amp; Ha\\x3c/h1\\x3e\\x3cscript\\x3e',
343
            Convert::raw2js('"; </script><h1>Ha &amp; Ha</h1><script>')
344
        );
345
        // Test newlines are properly escaped
346
        $this->assertEquals(
347
            'New\\nLine\\rReturn',
348
            Convert::raw2js("New\nLine\rReturn")
349
        );
350
        // Check escape of slashes
351
        $this->assertEquals(
352
            '\\\\\\"\\x3eClick here',
353
            Convert::raw2js('\\">Click here')
354
        );
355
    }
356
357
    /**
358
     * Tests {@link Convert::raw2json()}
359
     */
360
    public function testRaw2JSON()
361
    {
362
363
        // Test object
364
        $input = new stdClass();
365
        $input->Title = 'My Object';
366
        $input->Content = '<p>Data</p>';
367
        $this->assertEquals(
368
            '{"Title":"My Object","Content":"<p>Data<\/p>"}',
369
            Convert::raw2json($input)
370
        );
371
372
        // Array
373
        $array = array('One' => 'Apple', 'Two' => 'Banana');
374
        $this->assertEquals(
375
            '{"One":"Apple","Two":"Banana"}',
376
            Convert::raw2json($array)
377
        );
378
379
        // String value with already encoded data. Result should be quoted.
380
        $value = '{"Left": "Value"}';
381
        $this->assertEquals(
382
            '"{\\"Left\\": \\"Value\\"}"',
383
            Convert::raw2json($value)
384
        );
385
    }
386
387
    /**
388
     * Test that a context bitmask can be passed through to the json_encode method in {@link Convert::raw2json()}
389
     * and in {@link Convert::array2json()}
390
     */
391
    public function testRaw2JsonWithContext()
392
    {
393
        $data = array('foo' => 'b"ar');
394
        $expected = '{"foo":"b\u0022ar"}';
395
        $result = Convert::raw2json($data, JSON_HEX_QUOT);
396
        $this->assertSame($expected, $result);
397
        $wrapperResult = Convert::array2json($data, JSON_HEX_QUOT);
398
        $this->assertSame($expected, $wrapperResult);
399
    }
400
401
    /**
402
     * Tests {@link Convert::xml2array()}
403
     */
404
    public function testXML2Array()
405
    {
406
        // Ensure an XML file at risk of entity expansion can be avoided safely
407
        $inputXML = <<<XML
408
<?xml version="1.0"?>
409
<!DOCTYPE results [<!ENTITY long "SOME_SUPER_LONG_STRING">]>
410
<results>
411
    <result>Now include &long; lots of times to expand the in-memory size of this XML structure</result>
412
    <result>&long;&long;&long;</result>
413
</results>
414
XML
415
         ;
416
        try {
417
            Convert::xml2array($inputXML, true);
418
        } catch (Exception $ex) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
419
        }
420
        $this->assertTrue(
421
            isset($ex)
422
            && $ex instanceof InvalidArgumentException
423
            && $ex->getMessage() === 'XML Doctype parsing disabled'
424
        );
425
426
        // Test without doctype validation
427
        $expected = array(
428
         'result' => array(
429
          "Now include SOME_SUPER_LONG_STRING lots of times to expand the in-memory size of this XML structure",
430
          array(
431
        'long' => array(
432
         array(
433
          'long' => 'SOME_SUPER_LONG_STRING'
434
         ),
435
         array(
436
          'long' => 'SOME_SUPER_LONG_STRING'
437
         ),
438
         array(
439
          'long' => 'SOME_SUPER_LONG_STRING'
440
         )
441
           )
442
          )
443
         )
444
        );
445
        $result = Convert::xml2array($inputXML, false, true);
446
        $this->assertEquals(
447
            $expected,
448
            $result
449
        );
450
        $result = Convert::xml2array($inputXML, false, false);
451
        $this->assertEquals(
452
            $expected,
453
            $result
454
        );
455
    }
456
457
    /**
458
     * Tests {@link Convert::base64url_encode()} and {@link Convert::base64url_decode()}
459
     */
460
    public function testBase64url()
461
    {
462
        $data = 'Wëīrð characters ☺ such as ¤Ø¶÷╬';
463
        // This requires this test file to have UTF-8 character encoding
464
        $this->assertEquals(
465
            $data,
466
            Convert::base64url_decode(Convert::base64url_encode($data))
467
        );
468
469
        $data = 654.423;
470
        $this->assertEquals(
471
            $data,
472
            Convert::base64url_decode(Convert::base64url_encode($data))
473
        );
474
475
        $data = true;
476
        $this->assertEquals(
477
            $data,
478
            Convert::base64url_decode(Convert::base64url_encode($data))
479
        );
480
481
        $data = array('simple','array','¤Ø¶÷╬');
482
        $this->assertEquals(
483
            $data,
484
            Convert::base64url_decode(Convert::base64url_encode($data))
485
        );
486
487
        $data = array(
488
         'a'  => 'associative',
489
         4    => 'array',
490
         '☺' => '¤Ø¶÷╬'
491
        );
492
        $this->assertEquals(
493
            $data,
494
            Convert::base64url_decode(Convert::base64url_encode($data))
495
        );
496
    }
497
498
    public function testValidUtf8()
499
    {
500
        // Install a UTF-8 locale
501
        $this->previousLocaleSetting = setlocale(LC_CTYPE, 0);
502
503
        $locales = array('en_US.UTF-8', 'en_NZ.UTF-8', 'de_DE.UTF-8');
504
        $localeInstalled = false;
505
        foreach ($locales as $locale) {
506
            if ($localeInstalled = setlocale(LC_CTYPE, $locale)) {
507
                break;
508
            }
509
        }
510
511
        // If the system doesn't have any of the UTF-8 locales, exit early
512
        if ($localeInstalled === false) {
513
            $this->markTestIncomplete('Unable to run this test because of missing locale!');
514
            return;
515
        }
516
517
        $problematicText = html_entity_decode('<p>This is a&nbsp;Test with non-breaking&nbsp;space!</p>', ENT_COMPAT, 'UTF-8');
518
519
        $this->assertTrue(mb_check_encoding(Convert::html2raw($problematicText), 'UTF-8'));
520
    }
521
522
    public function testUpperCamelToLowerCamel()
523
    {
524
        $this->assertEquals(
525
            'd',
526
            Convert::upperCamelToLowerCamel('D'),
527
            'Single character'
528
        );
529
        $this->assertEquals(
530
            'id',
531
            Convert::upperCamelToLowerCamel('ID'),
532
            'Multi leading upper without trailing lower'
533
        );
534
        $this->assertEquals(
535
            'id',
536
            Convert::upperCamelToLowerCamel('Id'),
537
            'Single leading upper with trailing lower'
538
        );
539
        $this->assertEquals(
540
            'idField',
541
            Convert::upperCamelToLowerCamel('IdField'),
542
            'Single leading upper with trailing upper camel'
543
        );
544
        $this->assertEquals(
545
            'idField',
546
            Convert::upperCamelToLowerCamel('IDField'),
547
            'Multi leading upper with trailing upper camel'
548
        );
549
        $this->assertEquals(
550
            'iDField',
551
            Convert::upperCamelToLowerCamel('iDField'),
552
            'Single leading lower with trailing upper camel'
553
        );
554
        $this->assertEquals(
555
            '_IDField',
556
            Convert::upperCamelToLowerCamel('_IDField'),
557
            'Non-alpha leading  with trailing upper camel'
558
        );
559
    }
560
561
    /**
562
     * Test that memstring2bytes returns the number of bytes for a PHP ini style size declaration
563
     *
564
     * @param string $memString
565
     * @param int    $expected
566
     * @dataProvider memString2BytesProvider
567
     */
568
    public function testMemString2Bytes($memString, $expected)
569
    {
570
        $this->assertSame($expected, Convert::memstring2bytes($memString));
571
    }
572
573
    /**
574
     * @return array
575
     */
576
    public function memString2BytesProvider()
577
    {
578
        return [
579
            ['2048', (float)(2 * 1024)],
580
            ['2k', (float)(2 * 1024)],
581
            ['512M', (float)(512 * 1024 * 1024)],
582
            ['512MiB', (float)(512 * 1024 * 1024)],
583
            ['512 mbytes', (float)(512 * 1024 * 1024)],
584
            ['512 megabytes', (float)(512 * 1024 * 1024)],
585
            ['1024g', (float)(1024 * 1024 * 1024 * 1024)],
586
            ['1024G', (float)(1024 * 1024 * 1024 * 1024)]
587
        ];
588
    }
589
590
    /**
591
     * Test that bytes2memstring returns a binary prefixed string representing the number of bytes
592
     *
593
     * @param string $bytes
594
     * @param int    $expected
595
     * @dataProvider bytes2MemStringProvider
596
     */
597
    public function testBytes2MemString($bytes, $expected)
598
    {
599
        $this->assertSame($expected, Convert::bytes2memstring($bytes));
0 ignored issues
show
Bug introduced by
$bytes of type string is incompatible with the type double expected by parameter $bytes of SilverStripe\Core\Convert::bytes2memstring(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

599
        $this->assertSame($expected, Convert::bytes2memstring(/** @scrutinizer ignore-type */ $bytes));
Loading history...
600
    }
601
602
    /**
603
     * @return array
604
     */
605
    public function bytes2MemStringProvider()
606
    {
607
        return [
608
            [200, '200B'],
609
            [(2 * 1024), '2K'],
610
            [(512 * 1024 * 1024), '512M'],
611
            [(512 * 1024 * 1024 * 1024), '512G'],
612
            [(512 * 1024 * 1024 * 1024 * 1024), '512T'],
613
            [(512 * 1024 * 1024 * 1024 * 1024 * 1024), '512P']
614
        ];
615
    }
616
617
    public function providerTestSlashes()
618
    {
619
        return [
620
            ['bob/bob', '/', true, 'bob/bob'],
621
            ['\\bob/bob\\', '/', true, '/bob/bob/'],
622
            ['\\bob////bob\\/', '/', true, '/bob/bob/'],
623
            ['bob/bob', '\\', true, 'bob\\bob'],
624
            ['\\bob/bob\\', '\\', true, '\\bob\\bob\\'],
625
            ['\\bob////bob\\/', '\\', true, '\\bob\\bob\\'],
626
            ['bob/bob', '#', true, 'bob#bob'],
627
            ['\\bob/bob\\', '#', true, '#bob#bob#'],
628
            ['\\bob////bob\\/', '#', true, '#bob#bob#'],
629
            ['bob/bob', '/', false, 'bob/bob'],
630
            ['\\bob/bob\\', '/', false, '/bob/bob/'],
631
            ['\\bob////bob\\/', '/', false, '/bob////bob//'],
632
            ['bob/bob', '\\', false, 'bob\\bob'],
633
            ['\\bob/bob\\', '\\', false, '\\bob\\bob\\'],
634
            ['\\bob////bob\\/', '\\', false, '\\bob\\\\\\\\bob\\\\'],
635
            ['bob/bob', '#', false, 'bob#bob'],
636
            ['\\bob/bob\\', '#', false, '#bob#bob#'],
637
            ['\\bob////bob\\/', '#', false, '#bob####bob##'],
638
        ];
639
    }
640
641
    /**
642
     * @dataProvider providerTestSlashes
643
     * @param string $path
644
     * @param string $separator
645
     * @param bool $multiple
646
     * @param string $expected
647
     */
648
    public function testSlashes($path, $separator, $multiple, $expected)
649
    {
650
        $this->assertEquals($expected, Convert::slashes($path, $separator, $multiple));
651
    }
652
}
653