yiisoft /
yii-debug
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Yiisoft\Yii\Debug\Tests\Unit; |
||
| 6 | |||
| 7 | use DateTimeZone; |
||
| 8 | use PHPUnit\Framework\TestCase; |
||
| 9 | use stdClass; |
||
| 10 | use Yiisoft\Yii\Debug as D; |
||
| 11 | use Yiisoft\Yii\Debug\Dumper; |
||
| 12 | use Yiisoft\Yii\Debug\Tests\Support\Stub\ThreeProperties; |
||
| 13 | |||
| 14 | use function socket_create; |
||
| 15 | |||
| 16 | use const AF_INET; |
||
| 17 | use const SOCK_STREAM; |
||
| 18 | use const SOL_TCP; |
||
| 19 | |||
| 20 | final class DumperTest extends TestCase |
||
| 21 | { |
||
| 22 | /** |
||
| 23 | * @dataProvider asJsonObjectMapDataProvider |
||
| 24 | */ |
||
| 25 | public function testAsJsonObjectsMap(mixed $var, $expectedResult): void |
||
| 26 | { |
||
| 27 | $exportResult = Dumper::create($var)->asJsonObjectsMap(); |
||
| 28 | $this->assertEquals($expectedResult, $exportResult); |
||
| 29 | } |
||
| 30 | |||
| 31 | public static function asJsonObjectMapDataProvider(): iterable |
||
| 32 | { |
||
| 33 | $user = new stdClass(); |
||
| 34 | $user->id = 1; |
||
| 35 | $objectId = spl_object_id($user); |
||
| 36 | |||
| 37 | yield 'flat std class' => [ |
||
| 38 | $user, |
||
| 39 | <<<S |
||
| 40 | {"stdClass#{$objectId}":{"public \$id":1}} |
||
| 41 | S, |
||
| 42 | ]; |
||
| 43 | |||
| 44 | $decoratedUser = clone $user; |
||
| 45 | $decoratedUser->name = 'Name'; |
||
| 46 | $decoratedUser->originalUser = $user; |
||
| 47 | $decoratedObjectId = spl_object_id($decoratedUser); |
||
| 48 | |||
| 49 | yield 'nested std class' => [ |
||
| 50 | $decoratedUser, |
||
| 51 | <<<S |
||
| 52 | {"stdClass#{$decoratedObjectId}":{"public \$id":1,"public \$name":"Name","public \$originalUser":"object@stdClass#{$objectId}"},"stdClass#{$objectId}":{"public \$id":1}} |
||
| 53 | S, |
||
| 54 | ]; |
||
| 55 | |||
| 56 | $closureInsideObject = new stdClass(); |
||
| 57 | $closureObject = fn () => true; |
||
| 58 | $closureObjectId = spl_object_id($closureObject); |
||
| 59 | $closureInsideObject->closure = $closureObject; |
||
| 60 | $closureInsideObjectId = spl_object_id($closureInsideObject); |
||
| 61 | |||
| 62 | yield 'closure inside std class' => [ |
||
| 63 | $closureInsideObject, |
||
| 64 | <<<S |
||
| 65 | {"stdClass#{$closureInsideObjectId}":{"public \$closure":"fn () => true"},"Closure#{$closureObjectId}":"fn () => true"} |
||
| 66 | S, |
||
| 67 | ]; |
||
| 68 | } |
||
| 69 | |||
| 70 | /** |
||
| 71 | * @dataProvider jsonDataProvider() |
||
| 72 | */ |
||
| 73 | public function testAsJson($variable, string $result): void |
||
| 74 | { |
||
| 75 | $output = Dumper::create($variable)->asJson(); |
||
| 76 | $this->assertEqualsWithoutLE($result, $output); |
||
| 77 | } |
||
| 78 | |||
| 79 | public function testCacheDoesNotCoversObjectOutOfDumpDepth(): void |
||
| 80 | { |
||
| 81 | $object1 = new stdClass(); |
||
| 82 | $object1Id = spl_object_id($object1); |
||
| 83 | $object2 = new stdClass(); |
||
| 84 | |||
| 85 | $variable = [$object1, [[$object2]]]; |
||
| 86 | $expectedResult = sprintf('["object@stdClass#%d",["array (1 item) [...]"]]', $object1Id); |
||
| 87 | |||
| 88 | $dumper = Dumper::create($variable); |
||
| 89 | $actualResult = $dumper->asJson(2); |
||
| 90 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 91 | |||
| 92 | $map = $dumper->asJsonObjectsMap(2); |
||
| 93 | $this->assertEqualsWithoutLE( |
||
| 94 | <<<S |
||
| 95 | {"stdClass#{$object1Id}":"{stateless object}"} |
||
| 96 | S, |
||
| 97 | $map, |
||
| 98 | ); |
||
| 99 | } |
||
| 100 | |||
| 101 | public function testDepthLimitInObjectMap(): void |
||
| 102 | { |
||
| 103 | $variable = [1, []]; |
||
| 104 | $expectedResult = '"array (2 items) [...]"'; |
||
| 105 | |||
| 106 | $dumper = Dumper::create($variable); |
||
| 107 | $actualResult = $dumper->asJson(0); |
||
| 108 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 109 | |||
| 110 | $map = $dumper->asJsonObjectsMap(0); |
||
| 111 | $this->assertEqualsWithoutLE('[]', $map); |
||
| 112 | } |
||
| 113 | |||
| 114 | public function testObjectProvidesDebugInfoMethod(): void |
||
| 115 | { |
||
| 116 | $variable = new class () { |
||
| 117 | public function __debugInfo(): array |
||
| 118 | { |
||
| 119 | return ['test' => 'ok']; |
||
| 120 | } |
||
| 121 | }; |
||
| 122 | $expectedResult = sprintf( |
||
| 123 | '{"class@anonymous#%d":{"public $test":"ok"}}', |
||
| 124 | spl_object_id($variable), |
||
| 125 | ); |
||
| 126 | |||
| 127 | $dumper = Dumper::create($variable); |
||
| 128 | $actualResult = $dumper->asJson(2); |
||
| 129 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 130 | |||
| 131 | $map = $dumper->asJsonObjectsMap(2); |
||
| 132 | $this->assertEqualsWithoutLE($expectedResult, $map); |
||
| 133 | } |
||
| 134 | |||
| 135 | public function testStatelessObjectInlined(): void |
||
| 136 | { |
||
| 137 | $statelessObject = new stdClass(); |
||
| 138 | $statelessObjectId = spl_object_id($statelessObject); |
||
| 139 | |||
| 140 | $statefulObject = new stdClass(); |
||
| 141 | $statefulObject->id = 1; |
||
| 142 | $statefulObjectId = spl_object_id($statefulObject); |
||
| 143 | |||
| 144 | $variable = [$statelessObject, [$statefulObject]]; |
||
| 145 | $expectedResult = sprintf( |
||
| 146 | '["object@stdClass#%d",["stdClass#%d (...)"]]', |
||
| 147 | $statelessObjectId, |
||
| 148 | $statefulObjectId |
||
| 149 | ); |
||
| 150 | |||
| 151 | $dumper = Dumper::create($variable); |
||
| 152 | $actualResult = $dumper->asJson(2); |
||
| 153 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 154 | |||
| 155 | $map = $dumper->asJsonObjectsMap(3); |
||
| 156 | $this->assertEqualsWithoutLE( |
||
| 157 | <<<S |
||
| 158 | {"stdClass#{$statelessObjectId}":"{stateless object}","stdClass#{$statefulObjectId}":{"public \$id":1}} |
||
| 159 | S, |
||
| 160 | $map, |
||
| 161 | ); |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * @dataProvider dataDeepNestedArray |
||
| 166 | */ |
||
| 167 | public function testDeepNestedArray(array $variable, string $expectedResult): void |
||
| 168 | { |
||
| 169 | $actualResult = Dumper::create($variable)->asJson(2); |
||
| 170 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 171 | } |
||
| 172 | |||
| 173 | public static function dataDeepNestedArray(): iterable |
||
| 174 | { |
||
| 175 | yield 'singular' => [ |
||
| 176 | [[['test']]], |
||
| 177 | '[["array (1 item) [...]"]]', |
||
| 178 | ]; |
||
| 179 | |||
| 180 | yield 'plural' => [ |
||
| 181 | [[['test', 'test'], ['test']]], |
||
| 182 | '[["array (2 items) [...]","array (1 item) [...]"]]', |
||
| 183 | ]; |
||
| 184 | } |
||
| 185 | |||
| 186 | public function testDeepNestedObject(): void |
||
| 187 | { |
||
| 188 | $object = new ThreeProperties(); |
||
| 189 | $object->first = $object; |
||
| 190 | $variable = [[$object]]; |
||
| 191 | |||
| 192 | $output = Dumper::create($variable)->asJson(2); |
||
| 193 | $result = sprintf( |
||
| 194 | '[["%s#%d (...)"]]', |
||
| 195 | str_replace('\\', '\\\\', ThreeProperties::class), |
||
| 196 | spl_object_id($object), |
||
| 197 | ); |
||
| 198 | $this->assertEqualsWithoutLE($result, $output); |
||
| 199 | } |
||
| 200 | |||
| 201 | public function testObjectVisibilityProperties(): void |
||
| 202 | { |
||
| 203 | $variable = new ThreeProperties(); |
||
| 204 | |||
| 205 | $output = Dumper::create($variable)->asJson(2); |
||
| 206 | $result = sprintf( |
||
| 207 | '{"%s#%d":{"public $first":"first","protected $second":"second","private $third":"third"}}', |
||
| 208 | str_replace('\\', '\\\\', ThreeProperties::class), |
||
| 209 | spl_object_id($variable), |
||
| 210 | ); |
||
| 211 | $this->assertEqualsWithoutLE($result, $output); |
||
| 212 | } |
||
| 213 | |||
| 214 | public function testFormatJson(): void |
||
| 215 | { |
||
| 216 | $variable = [['test']]; |
||
| 217 | |||
| 218 | $output = Dumper::create($variable)->asJson(2, true); |
||
| 219 | $result = <<<S |
||
| 220 | [ |
||
| 221 | [ |
||
| 222 | "test" |
||
| 223 | ] |
||
| 224 | ] |
||
| 225 | S; |
||
| 226 | $this->assertEqualsWithoutLE($result, $output); |
||
| 227 | } |
||
| 228 | |||
| 229 | public function testExcludedClasses(): void |
||
| 230 | { |
||
| 231 | $object1 = new stdClass(); |
||
| 232 | $object1Id = spl_object_id($object1); |
||
| 233 | $object1Class = $object1::class; |
||
| 234 | |||
| 235 | $object2 = new DateTimeZone('UTC'); |
||
| 236 | $object2Id = spl_object_id($object2); |
||
| 237 | $object2Class = $object2::class; |
||
| 238 | |||
| 239 | $actualResult = Dumper::create([$object1, $object2], [$object1Class])->asJson(2, true); |
||
| 240 | $expectedResult = <<<S |
||
| 241 | [ |
||
| 242 | "{$object1Class}#{$object1Id} (...)", |
||
| 243 | "object@{$object2Class}#{$object2Id}" |
||
| 244 | ] |
||
| 245 | S; |
||
| 246 | |||
| 247 | $this->assertEqualsWithoutLE($expectedResult, $actualResult); |
||
| 248 | } |
||
| 249 | |||
| 250 | public static function jsonDataProvider(): iterable |
||
| 251 | { |
||
| 252 | $emptyObject = new stdClass(); |
||
| 253 | $emptyObjectId = spl_object_id($emptyObject); |
||
| 254 | |||
| 255 | yield 'empty object' => [ |
||
| 256 | $emptyObject, |
||
| 257 | <<<S |
||
| 258 | {"stdClass#{$emptyObjectId}":"{stateless object}"} |
||
| 259 | S, |
||
| 260 | ]; |
||
| 261 | |||
| 262 | // @formatter:off |
||
| 263 | $shortFunctionObject = fn () => 1; |
||
| 264 | // @formatter:on |
||
| 265 | $shortFunctionObjectId = spl_object_id($shortFunctionObject); |
||
| 266 | |||
| 267 | yield 'short function' => [ |
||
| 268 | $shortFunctionObject, |
||
| 269 | <<<S |
||
| 270 | {"Closure#{$shortFunctionObjectId}":"fn () => 1"} |
||
| 271 | S, |
||
| 272 | ]; |
||
| 273 | |||
| 274 | // @formatter:off |
||
| 275 | $staticShortFunctionObject = static fn () => 1; |
||
| 276 | // @formatter:on |
||
| 277 | $staticShortFunctionObjectId = spl_object_id($staticShortFunctionObject); |
||
| 278 | |||
| 279 | yield 'short static function' => [ |
||
| 280 | $staticShortFunctionObject, |
||
| 281 | <<<S |
||
| 282 | {"Closure#{$staticShortFunctionObjectId}":"static fn () => 1"} |
||
| 283 | S, |
||
| 284 | ]; |
||
| 285 | |||
| 286 | // @formatter:off |
||
| 287 | $functionObject = function () { |
||
| 288 | return 1; |
||
| 289 | }; |
||
| 290 | // @formatter:on |
||
| 291 | $functionObjectId = spl_object_id($functionObject); |
||
| 292 | |||
| 293 | yield 'function' => [ |
||
| 294 | $functionObject, |
||
| 295 | <<<S |
||
| 296 | {"Closure#{$functionObjectId}":"function () {\\n return 1;\\n}"} |
||
| 297 | S, |
||
| 298 | ]; |
||
| 299 | |||
| 300 | // @formatter:off |
||
| 301 | $staticFunctionObject = static function () { |
||
| 302 | return 1; |
||
| 303 | }; |
||
| 304 | // @formatter:on |
||
| 305 | $staticFunctionObjectId = spl_object_id($staticFunctionObject); |
||
| 306 | |||
| 307 | yield 'static function' => [ |
||
| 308 | $staticFunctionObject, |
||
| 309 | <<<S |
||
| 310 | {"Closure#{$staticFunctionObjectId}":"static function () {\\n return 1;\\n}"} |
||
| 311 | S, |
||
| 312 | ]; |
||
| 313 | yield 'string' => [ |
||
| 314 | 'Hello, Yii!', |
||
| 315 | '"Hello, Yii!"', |
||
| 316 | ]; |
||
| 317 | yield 'empty string' => [ |
||
| 318 | '', |
||
| 319 | '""', |
||
| 320 | ]; |
||
| 321 | yield 'null' => [ |
||
| 322 | null, |
||
| 323 | 'null', |
||
| 324 | ]; |
||
| 325 | yield 'integer' => [ |
||
| 326 | 1, |
||
| 327 | '1', |
||
| 328 | ]; |
||
| 329 | yield 'integer with separator' => [ |
||
| 330 | 1_23_456, |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 331 | '123456', |
||
| 332 | ]; |
||
| 333 | yield 'boolean' => [ |
||
| 334 | true, |
||
| 335 | 'true', |
||
| 336 | ]; |
||
| 337 | yield 'fileResource' => [ |
||
| 338 | fopen('php://input', 'rb'), |
||
| 339 | '{"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"PHP","stream_type":"Input","mode":"rb","unread_bytes":0,"seekable":true,"uri":"php:\/\/input"}', |
||
| 340 | ]; |
||
| 341 | yield 'empty array' => [ |
||
| 342 | [], |
||
| 343 | '[]', |
||
| 344 | ]; |
||
| 345 | yield 'array of 3 elements, automatic keys' => [ |
||
| 346 | [ |
||
| 347 | 'one', |
||
| 348 | 'two', |
||
| 349 | 'three', |
||
| 350 | ], |
||
| 351 | '["one","two","three"]', |
||
| 352 | ]; |
||
| 353 | yield 'array of 3 elements, custom keys' => [ |
||
| 354 | [ |
||
| 355 | 2 => 'one', |
||
| 356 | 'two' => 'two', |
||
| 357 | 0 => 'three', |
||
| 358 | ], |
||
| 359 | '{"2":"one","two":"two","0":"three"}', |
||
| 360 | ]; |
||
| 361 | |||
| 362 | // @formatter:off |
||
| 363 | $closureInArrayObject = fn () => new \DateTimeZone(''); |
||
| 364 | // @formatter:on |
||
| 365 | $closureInArrayObjectId = spl_object_id($closureInArrayObject); |
||
| 366 | |||
| 367 | yield 'closure in array' => [ |
||
| 368 | // @formatter:off |
||
| 369 | [$closureInArrayObject], |
||
| 370 | // @formatter:on |
||
| 371 | <<<S |
||
| 372 | [{"Closure#{$closureInArrayObjectId}":"fn () => new \\\DateTimeZone('')"}] |
||
| 373 | S, |
||
| 374 | ]; |
||
| 375 | |||
| 376 | // @formatter:off |
||
| 377 | $closureWithUsualClassNameObject = fn (Dumper $date) => new \DateTimeZone(''); |
||
| 378 | // @formatter:on |
||
| 379 | $closureWithUsualClassNameObjectId = spl_object_id($closureWithUsualClassNameObject); |
||
| 380 | |||
| 381 | yield 'original class name' => [ |
||
| 382 | $closureWithUsualClassNameObject, |
||
| 383 | <<<S |
||
| 384 | {"Closure#{$closureWithUsualClassNameObjectId}":"fn (\\\Yiisoft\\\Yii\\\Debug\\\Dumper \$date) => new \\\DateTimeZone('')"} |
||
| 385 | S, |
||
| 386 | ]; |
||
| 387 | |||
| 388 | // @formatter:off |
||
| 389 | $closureWithAliasedClassNameObject = fn (Dumper $date) => new \DateTimeZone(''); |
||
| 390 | // @formatter:on |
||
| 391 | $closureWithAliasedClassNameObjectId = spl_object_id($closureWithAliasedClassNameObject); |
||
| 392 | |||
| 393 | yield 'class alias' => [ |
||
| 394 | $closureWithAliasedClassNameObject, |
||
| 395 | <<<S |
||
| 396 | {"Closure#{$closureWithAliasedClassNameObjectId}":"fn (\\\Yiisoft\\\Yii\\\Debug\\\Dumper \$date) => new \\\DateTimeZone('')"} |
||
| 397 | S, |
||
| 398 | ]; |
||
| 399 | |||
| 400 | // @formatter:off |
||
| 401 | $closureWithAliasedNamespaceObject = fn (D\Dumper $date) => new \DateTimeZone(''); |
||
| 402 | // @formatter:on |
||
| 403 | $closureWithAliasedNamespaceObjectId = spl_object_id($closureWithAliasedNamespaceObject); |
||
| 404 | |||
| 405 | yield 'namespace alias' => [ |
||
| 406 | $closureWithAliasedNamespaceObject, |
||
| 407 | <<<S |
||
| 408 | {"Closure#{$closureWithAliasedNamespaceObjectId}":"fn (\\\Yiisoft\\\Yii\\\Debug\\\Dumper \$date) => new \\\DateTimeZone('')"} |
||
| 409 | S, |
||
| 410 | ]; |
||
| 411 | // @formatter:off |
||
| 412 | $closureWithNullCollisionOperatorObject = fn () => $_ENV['var'] ?? null; |
||
| 413 | // @formatter:on |
||
| 414 | $closureWithNullCollisionOperatorObjectId = spl_object_id($closureWithNullCollisionOperatorObject); |
||
| 415 | |||
| 416 | yield 'closure with null-collision operator' => [ |
||
| 417 | $closureWithNullCollisionOperatorObject, |
||
| 418 | <<<S |
||
| 419 | {"Closure#{$closureWithNullCollisionOperatorObjectId}":"fn () => \$_ENV['var'] ?? null"} |
||
| 420 | S, |
||
| 421 | ]; |
||
| 422 | yield 'utf8 supported' => [ |
||
| 423 | '🤣', |
||
| 424 | '"🤣"', |
||
| 425 | ]; |
||
| 426 | |||
| 427 | |||
| 428 | $objectWithClosureInProperty = new stdClass(); |
||
| 429 | // @formatter:off |
||
| 430 | $objectWithClosureInProperty->a = fn () => 1; |
||
| 431 | // @formatter:on |
||
| 432 | $objectWithClosureInPropertyId = spl_object_id($objectWithClosureInProperty); |
||
| 433 | $objectWithClosureInPropertyClosureId = spl_object_id($objectWithClosureInProperty->a); |
||
| 434 | |||
| 435 | yield 'closure in property supported' => [ |
||
| 436 | $objectWithClosureInProperty, |
||
| 437 | <<<S |
||
| 438 | {"stdClass#{$objectWithClosureInPropertyId}":{"public \$a":{"Closure#{$objectWithClosureInPropertyClosureId}":"fn () => 1"}}} |
||
| 439 | S, |
||
| 440 | ]; |
||
| 441 | yield 'binary string' => [ |
||
| 442 | pack('H*', md5('binary string')), |
||
| 443 | '"ɍ��^��\u00191\u0017�]�-f�"', |
||
| 444 | ]; |
||
| 445 | |||
| 446 | |||
| 447 | $fileResource = tmpfile(); |
||
| 448 | $fileResourceUri = stream_get_meta_data($fileResource)['uri']; |
||
| 449 | $fileResourceUri = addcslashes($fileResourceUri, '/\\'); |
||
| 450 | |||
| 451 | yield 'file resource' => [ |
||
| 452 | $fileResource, |
||
| 453 | <<<S |
||
| 454 | {"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"plainfile","stream_type":"STDIO","mode":"r+b","unread_bytes":0,"seekable":true,"uri":"{$fileResourceUri}"} |
||
| 455 | S, |
||
| 456 | ]; |
||
| 457 | |||
| 458 | $closedFileResource = tmpfile(); |
||
| 459 | fclose($closedFileResource); |
||
| 460 | |||
| 461 | yield 'closed file resource' => [ |
||
| 462 | $closedFileResource, |
||
| 463 | '"{closed resource}"', |
||
| 464 | ]; |
||
| 465 | |||
| 466 | $socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); |
||
| 467 | $socketResourceId = spl_object_id($socketResource); |
||
| 468 | yield 'socket resource' => [ |
||
| 469 | $socketResource, |
||
| 470 | <<<S |
||
| 471 | {"Socket#{$socketResourceId}":"{stateless object}"} |
||
| 472 | S, |
||
| 473 | ]; |
||
| 474 | |||
| 475 | $opendirResource = opendir(sys_get_temp_dir()); |
||
| 476 | |||
| 477 | yield 'opendir resource' => [ |
||
| 478 | $opendirResource, |
||
| 479 | <<<S |
||
| 480 | {"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"plainfile","stream_type":"dir","mode":"r","unread_bytes":0,"seekable":true} |
||
| 481 | S, |
||
| 482 | ]; |
||
| 483 | |||
| 484 | $curlResource = curl_init('https://example.com'); |
||
| 485 | $curlResourceObjectId = spl_object_id($curlResource); |
||
| 486 | |||
| 487 | yield 'curl resource' => [ |
||
| 488 | $curlResource, |
||
| 489 | <<<S |
||
| 490 | {"CurlHandle#{$curlResourceObjectId}":"{stateless object}"} |
||
| 491 | S, |
||
| 492 | ]; |
||
| 493 | yield 'stdout' => [ |
||
| 494 | STDOUT, |
||
| 495 | <<<S |
||
| 496 | {"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"PHP","stream_type":"STDIO","mode":"wb","unread_bytes":0,"seekable":false,"uri":"php:\/\/stdout"} |
||
| 497 | S, |
||
| 498 | ]; |
||
| 499 | yield 'stderr' => [ |
||
| 500 | STDERR, |
||
| 501 | <<<S |
||
| 502 | {"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"PHP","stream_type":"STDIO","mode":"wb","unread_bytes":0,"seekable":false,"uri":"php:\/\/stderr"} |
||
| 503 | S, |
||
| 504 | ]; |
||
| 505 | yield 'stdin' => [ |
||
| 506 | STDIN, |
||
| 507 | <<<S |
||
| 508 | {"timed_out":false,"blocked":true,"eof":false,"wrapper_type":"PHP","stream_type":"STDIO","mode":"rb","unread_bytes":0,"seekable":false,"uri":"php:\/\/stdin"} |
||
| 509 | S, |
||
| 510 | ]; |
||
| 511 | } |
||
| 512 | |||
| 513 | /** |
||
| 514 | * Asserting two strings equality ignoring line endings. |
||
| 515 | */ |
||
| 516 | protected function assertEqualsWithoutLE(string $expected, string $actual, string $message = ''): void |
||
| 517 | { |
||
| 518 | $expected = str_replace(["\r\n", '\r\n'], ["\n", '\n'], $expected); |
||
| 519 | $actual = str_replace(["\r\n", '\r\n'], ["\n", '\n'], $actual); |
||
| 520 | $this->assertEquals($expected, $actual, $message); |
||
| 521 | } |
||
| 522 | } |
||
| 523 |