1<?php 2 3/** 4 * Hoa 5 * 6 * 7 * @license 8 * 9 * New BSD License 10 * 11 * Copyright © 2007-2017, Hoa community. All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions are met: 15 * * Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * * Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * * Neither the name of the Hoa nor the names of its contributors may be 21 * used to endorse or promote products derived from this software without 22 * specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37namespace Hoa\Stream\Test\Unit; 38 39use Hoa\Event; 40use Hoa\Stream as LUT; 41use Hoa\Test; 42 43/** 44 * Class \Hoa\Stream\Test\Unit\Stream. 45 * 46 * Test suite of the stream class. 47 * 48 * @copyright Copyright © 2007-2017 Hoa community 49 * @license New BSD License 50 */ 51class Stream extends Test\Unit\Suite 52{ 53 public function case_interfaces() 54 { 55 $this 56 ->when($result = new SUT(__FILE__)) 57 ->then 58 ->object($result) 59 ->isInstanceOf(LUT\IStream\Stream::class) 60 ->isInstanceOf(Event\Listenable::class); 61 } 62 63 public function case_constants() 64 { 65 $this 66 ->integer(SUT::NAME) 67 ->isEqualTo(0) 68 ->integer(SUT::HANDLER) 69 ->isEqualTo(1) 70 ->integer(SUT::RESOURCE) 71 ->isEqualTo(2) 72 ->integer(SUT::CONTEXT) 73 ->isEqualTo(3); 74 } 75 76 public function case_construct() 77 { 78 $this 79 ->given($name = __FILE__) 80 ->when($result = new SUT($name)) 81 ->then 82 ->string($result->getStreamName()) 83 ->isEqualTo($name) 84 ->boolean($this->invoke($result)->hasBeenDeferred()) 85 ->isFalse() 86 ->let($listener = $this->invoke($result)->getListener()) 87 ->object($listener) 88 ->isInstanceOf(Event\Listener::class) 89 ->boolean($listener->listenerExists('authrequire')) 90 ->isTrue() 91 ->boolean($listener->listenerExists('authresult')) 92 ->isTrue() 93 ->boolean($listener->listenerExists('complete')) 94 ->isTrue() 95 ->boolean($listener->listenerExists('connect')) 96 ->isTrue() 97 ->boolean($listener->listenerExists('failure')) 98 ->isTrue() 99 ->boolean($listener->listenerExists('mimetype')) 100 ->isTrue() 101 ->boolean($listener->listenerExists('progress')) 102 ->isTrue() 103 ->boolean($listener->listenerExists('redirect')) 104 ->isTrue() 105 ->boolean($listener->listenerExists('resolve')) 106 ->isTrue() 107 ->boolean($listener->listenerExists('size')) 108 ->isTrue() 109 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name)) 110 ->isTrue() 111 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name . ':close-before')) 112 ->isTrue(); 113 } 114 115 public function case_construct_with_a_context() 116 { 117 $this 118 ->given( 119 $name = __FILE__, 120 $contextName = 'foo', 121 LUT\Context::getInstance($contextName) 122 ) 123 ->when($result = new SUT($name, $contextName)) 124 ->then 125 ->string($result->getStreamName()) 126 ->isEqualTo($name) 127 ->boolean($this->invoke($result)->hasBeenDeferred()) 128 ->isFalse() 129 ->object($this->invoke($result)->getListener()) 130 ->isInstanceOf(Event\Listener::class); 131 } 132 133 public function case_construct_with_deferred_opening() 134 { 135 $this 136 ->given($name = __FILE__) 137 ->when($result = new SUT($name, null, true)) 138 ->then 139 ->boolean($this->invoke($result)->hasBeenDeferred()) 140 ->isTrue() 141 ->boolean($result->isOpened()) 142 ->isFalse() 143 ->variable($result->getStreamName()) 144 ->isNull(); 145 } 146 147 public function case_open() 148 { 149 $this 150 ->given( 151 $name = __FILE__, 152 $stream = new SUT($name, null, true) 153 ) 154 ->when($result = $stream->open()) 155 ->then 156 ->object($result) 157 ->isIdenticalTo($stream) 158 ->boolean($this->invoke($result)->hasBeenDeferred()) 159 ->isTrue() 160 ->boolean($result->isOpened()) 161 ->isTrue() 162 ->string($result->getStreamName()) 163 ->isEqualTo($name) 164 ->integer($result->getStreamBufferSize()) 165 ->isEqualTo(SUT::DEFAULT_BUFFER_SIZE); 166 } 167 168 public function case_close() 169 { 170 $this 171 ->given( 172 $name = __FILE__, 173 $stream = new SUT($name), 174 $resource = $stream->getStream(), 175 $context = $stream->getStreamContext() 176 ) 177 ->when($result = $stream->close()) 178 ->then 179 ->variable($result) 180 ->isNull() 181 ->boolean($stream->isOpened()) 182 ->isFalse() 183 ->variable(SUT::getStreamHandler($stream)) 184 ->isNull() 185 ->variable($stream->getStreamName()) 186 ->isEqualTo($name) 187 ->variable($stream->getStream()) 188 ->isEqualTo($resource) 189 ->variable($stream->getStreamContext()) 190 ->isEqualTo($context) 191 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name)) 192 ->isFalse() 193 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name . ':close-before')) 194 ->isFalse(); 195 } 196 197 public function case_close_more_than_once() 198 { 199 $this 200 ->given( 201 $name = __FILE__, 202 $stream = new SUT($name), 203 $close1 = $stream->close() 204 ) 205 ->when($result = $stream->close()) 206 ->then 207 ->variable($result) 208 ->isIdenticalTo($close1); 209 } 210 211 public function case_open_close_open() 212 { 213 $this 214 ->given( 215 $name = __FILE__, 216 $stream = new SUT($name, null, true), 217 $stream->open(), 218 $resource = $stream->getStream(), 219 $context = $stream->getStreamContext(), 220 $handler = SUT::getStreamHandler($stream), 221 222 $this->function->stream_set_write_buffer = 0, 223 224 $stream->setStreamBuffer(42), 225 $stream->close() 226 ) 227 ->when($result = $stream->open()) 228 ->then 229 ->string($result->getStreamName()) 230 ->isEqualTo($name) 231 ->resource($result->getStream()) 232 ->isNotEqualTo($resource) 233 ->object($handler) 234 ->isIdenticalTo($result) 235 ->object($this->invoke($stream)->getListener()) 236 ->isInstanceOf(Event\Listener::class) 237 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name)) 238 ->isTrue() 239 ->boolean(Event::eventExists('hoa://Event/Stream/' . $name . ':close-before')) 240 ->isTrue() 241 ->integer($stream->getStreamBufferSize()) 242 ->isEqualTo(SUT::DEFAULT_BUFFER_SIZE); 243 } 244 245 public function case_close_event_close_before() 246 { 247 $self = $this; 248 249 $this 250 ->given( 251 $name = 'hoa://Test/Vfs/Foo?type=file', 252 $stream = new SUT($name), 253 Event::getEvent('hoa://Event/Stream/' . $name . ':close-before')->attach( 254 function (Event\Bucket $bucket) use ($self, &$called) { 255 $called = true; 256 257 $self 258 ->variable($bucket->getData()) 259 ->isNull() 260 ->boolean($bucket->getSource()->isOpened()) 261 ->isTrue(); 262 } 263 ) 264 ) 265 ->when($result = $stream->close()) 266 ->then 267 ->boolean($called) 268 ->isTrue(); 269 } 270 271 public function case_get_stream_name() 272 { 273 $this 274 ->given( 275 $name = __FILE__, 276 $stream = new SUT($name) 277 ) 278 ->when($result = $stream->getStreamName()) 279 ->then 280 ->string($result) 281 ->isEqualTo($name); 282 } 283 284 public function case_get_stream() 285 { 286 $this 287 ->given( 288 $name = __FILE__, 289 $stream = new SUT($name) 290 ) 291 ->when($result = $stream->getStream()) 292 ->then 293 ->resource($result) 294 ->isStream($name); 295 } 296 297 public function case_get_stream_context() 298 { 299 $this 300 ->given( 301 $name = __FILE__, 302 $contextName = 'foo', 303 $context = LUT\Context::getInstance($contextName), 304 $stream = new SUT($name, $contextName) 305 ) 306 ->when($result = $stream->getStreamContext()) 307 ->then 308 ->object($result) 309 ->isIdenticalTo($context); 310 } 311 312 public function case_get_stream_context_with_no_context_given() 313 { 314 $this 315 ->given( 316 $name = __FILE__, 317 $stream = new SUT($name) 318 ) 319 ->when($result = $stream->getStreamContext()) 320 ->then 321 ->variable($result) 322 ->isNull(); 323 } 324 325 public function case_get_stream_handler() 326 { 327 $this 328 ->given( 329 $name = __FILE__, 330 $stream = new SUT($name) 331 ) 332 ->when($result = SUT::getStreamHandler($name)) 333 ->then 334 ->object($result) 335 ->isIdenticalTo($result); 336 } 337 338 public function case_get_stream_handler_of_unknown_stream() 339 { 340 $this 341 ->when($result = SUT::getStreamHandler('foo')) 342 ->then 343 ->variable($result) 344 ->isNull(); 345 } 346 347 public function case__set_stream() 348 { 349 $this 350 ->given( 351 $stream = new SUT(__FILE__), 352 $oldStream = $stream->getStream(), 353 $newStream = fopen('php://memory', 'rb') 354 ) 355 ->when($result = $stream->_setStream($newStream)) 356 ->then 357 ->resource($result) 358 ->isIdenticalTo($oldStream) 359 ->isStream() 360 ->resource($stream->getStream()) 361 ->isStream() 362 ->isIdenticalTo($newStream); 363 } 364 365 public function case__set_stream_invalid_resource() 366 { 367 $this 368 ->given($stream = new SUT(__FILE__)) 369 ->exception(function () use ($stream) { 370 $stream->_setStream(true); 371 }) 372 ->isInstanceOf(LUT\Exception::class); 373 } 374 375 public function case__set_stream_unknown_resource() 376 { 377 $this 378 ->given( 379 $stream = new SUT(__FILE__), 380 $oldStream = $stream->getStream(), 381 $newStream = fopen('php://memory', 'rb'), 382 $this->function->is_resource = false, 383 $this->function->gettype = 'resource', 384 $this->function->get_resource_type = 'Unknown' 385 ) 386 ->when($result = $stream->_setStream($newStream)) 387 ->then 388 ->resource($result) 389 ->isIdenticalTo($oldStream) 390 ->isStream() 391 ->resource($stream->getStream()) 392 ->isStream() 393 ->isIdenticalTo($newStream); 394 } 395 396 public function case_is_opened() 397 { 398 $this 399 ->given($stream = new SUT(__FILE__)) 400 ->when($result = $stream->isOpened()) 401 ->then 402 ->boolean($result) 403 ->isTrue(); 404 } 405 406 public function case_is_not_opened() 407 { 408 $this 409 ->given($stream = new SUT(__FILE__, null, true)) 410 ->when($result = $stream->isOpened()) 411 ->then 412 ->boolean($result) 413 ->isFalse() 414 415 ->when( 416 $stream->open(), 417 $result = $stream->isOpened() 418 ) 419 ->then 420 ->boolean($result) 421 ->isTrue(); 422 } 423 424 public function case_set_stream_timeout() 425 { 426 $self = $this; 427 428 $this 429 ->given( 430 $stream = new SUT(__FILE__), 431 432 $this->function->stream_set_timeout = function ($_stream, $_seconds, $_microseconds) use ($self, $stream, &$called) { 433 $called = true; 434 435 $self 436 ->resource($_stream) 437 ->isIdenticalTo($stream->getStream()) 438 ->integer($_seconds) 439 ->isEqualTo(7) 440 ->integer($_microseconds) 441 ->isEqualTo(42); 442 443 return true; 444 } 445 ) 446 ->when($result = $stream->setStreamTimeout(7, 42)) 447 ->then 448 ->boolean($result) 449 ->isTrue() 450 ->boolean($called) 451 ->isTrue(); 452 } 453 454 public function case_has_been_deferred() 455 { 456 $this 457 ->given($stream = new SUT(__FILE__, null, true)) 458 ->when($result = $this->invoke($stream)->hasBeenDeferred()) 459 ->then 460 ->boolean($result) 461 ->isTrue(); 462 } 463 464 public function case_has_not_been_deferred() 465 { 466 $this 467 ->given($stream = new SUT(__FILE__)) 468 ->when($result = $this->invoke($stream)->hasBeenDeferred()) 469 ->then 470 ->boolean($result) 471 ->isFalse(); 472 } 473 474 public function case_has_timed_out() 475 { 476 $this 477 ->given( 478 $stream = new SUT(__FILE__), 479 $this->function->stream_get_meta_data = [ 480 'timed_out' => true 481 ] 482 ) 483 ->when($result = $stream->hasTimedOut()) 484 ->then 485 ->boolean($result) 486 ->isTrue(); 487 } 488 489 public function case_has_not_timed_out() 490 { 491 $this 492 ->given( 493 $stream = new SUT(__FILE__), 494 $this->function->stream_get_meta_data = [ 495 'timed_out' => false 496 ] 497 ) 498 ->when($result = $stream->hasTimedOut()) 499 ->then 500 ->boolean($result) 501 ->isFalse(); 502 } 503 504 public function case_set_stream_blocking() 505 { 506 $self = $this; 507 508 $this 509 ->given( 510 $stream = new SUT(__FILE__), 511 512 $this->function->stream_set_blocking = function ($_stream, $_mode) use ($self, $stream, &$called) { 513 $called = true; 514 515 $self 516 ->resource($_stream) 517 ->isIdenticalTo($stream->getStream()) 518 ->integer($_mode) 519 ->isEqualTo(1); 520 521 return true; 522 } 523 ) 524 ->when($result = $stream->setStreamBlocking(true)) 525 ->then 526 ->boolean($result) 527 ->isTrue() 528 ->boolean($called) 529 ->isTrue(); 530 } 531 532 public function case_get_default_stream_buffer_size() 533 { 534 $self = $this; 535 536 $this 537 ->given($stream = new SUT(__FILE__)) 538 ->when($result = $stream->getStreamBufferSize()) 539 ->then 540 ->integer($result) 541 ->isEqualTo(8192); 542 } 543 544 public function case_set_stream_buffer() 545 { 546 $self = $this; 547 548 $this 549 ->given( 550 $stream = new SUT(__FILE__), 551 552 $this->function->stream_set_write_buffer = function ($_stream, $_buffer) use ($self, $stream, &$called) { 553 $called = true; 554 555 $self 556 ->resource($_stream) 557 ->isIdenticalTo($stream->getStream()) 558 ->integer($_buffer) 559 ->isEqualTo(42); 560 561 return 0; 562 } 563 ) 564 ->when($result = $stream->setStreamBuffer(42)) 565 ->then 566 ->boolean($result) 567 ->isTrue() 568 ->boolean($called) 569 ->isTrue() 570 ->integer($stream->getStreamBufferSize()) 571 ->isEqualTo(42); 572 } 573 574 public function case_set_stream_buffer_fail() 575 { 576 $self = $this; 577 578 $this 579 ->given( 580 $stream = new SUT(__FILE__), 581 $oldStreamBufferSize = $stream->getStreamBufferSize(), 582 583 $this->function->stream_set_write_buffer = function ($_stream, $_buffer) use ($self, $stream, &$called) { 584 $called = true; 585 586 $self 587 ->resource($_stream) 588 ->isIdenticalTo($stream->getStream()) 589 ->integer($_buffer) 590 ->isEqualTo(42); 591 592 return 1; 593 } 594 ) 595 ->when($result = $stream->setStreamBuffer(42)) 596 ->then 597 ->boolean($result) 598 ->isFalse() 599 ->boolean($called) 600 ->isTrue() 601 ->integer($stream->getStreamBufferSize()) 602 ->isEqualTo($oldStreamBufferSize); 603 } 604 605 public function case_disable_stream_buffer() 606 { 607 $self = $this; 608 609 $this 610 ->given( 611 $stream = new SUT(__FILE__), 612 613 $this->function->stream_set_write_buffer = function ($_stream, $_buffer) use ($self, $stream, &$called) { 614 $called = true; 615 616 $self 617 ->resource($_stream) 618 ->isIdenticalTo($stream->getStream()) 619 ->integer($_buffer) 620 ->isEqualTo(0); 621 622 return 0; 623 } 624 ) 625 ->when($result = $stream->disableStreamBuffer()) 626 ->then 627 ->boolean($result) 628 ->isTrue() 629 ->boolean($called) 630 ->isTrue() 631 ->integer($stream->getStreamBufferSize()) 632 ->isEqualTo(0); 633 } 634 635 public function case_get_stream_wrapper_name_with_no_wrapper() 636 { 637 $this 638 ->given($stream = new SUT(__FILE__)) 639 ->when($result = $stream->getStreamWrapperName()) 640 ->then 641 ->string($result) 642 ->isEqualTo('file'); 643 } 644 645 public function case_get_stream_wrapper_name() 646 { 647 $this 648 ->given($stream = new SUT('hoa://Test/Vfs/Foo?type=file')) 649 ->when($result = $stream->getStreamWrapperName()) 650 ->then 651 ->string($result) 652 ->isEqualTo('hoa'); 653 } 654 655 public function case_get_stream_meta_data() 656 { 657 $this 658 ->given($stream = new SUT(__FILE__)) 659 ->when($result = $stream->getStreamMetaData()) 660 ->then 661 ->array($result) 662 ->isEqualTo([ 663 'timed_out' => false, 664 'blocked' => true, 665 'eof' => false, 666 'wrapper_type' => 'plainfile', 667 'stream_type' => 'STDIO', 668 'mode' => 'rb', 669 'unread_bytes' => 0, 670 'seekable' => true, 671 'uri' => __FILE__ 672 ]); 673 } 674 675 public function case_is_borrowing() 676 { 677 $this 678 ->given( 679 $streamA1 = new SUT(__FILE__), 680 $streamA2 = new SUT(__FILE__) 681 ) 682 ->when($result = $streamA2->isBorrowing()) 683 ->then 684 ->boolean($result) 685 ->isTrue() 686 ->boolean($streamA1->isBorrowing()) 687 ->isFalse(); 688 } 689 690 public function case_is_not_borrowing() 691 { 692 $this 693 ->given($stream = new SUT(__FILE__)) 694 ->when($result = $stream->isBorrowing()) 695 ->then 696 ->boolean($result) 697 ->isFalse(); 698 } 699 700 public function case_shutdown_destructor() 701 { 702 $this 703 ->given( 704 $stream = new \Mock\Hoa\Stream\Test\Unit\SUTWithPublicClose(__FILE__), 705 $this->calling($stream)->_close = function () use (&$called) { 706 $called = true; 707 } 708 ) 709 ->when($result = SUT::_Hoa_Stream()) 710 ->then 711 ->boolean($called) 712 ->isTrue(); 713 } 714 715 public function case_destruct_an_opened_stream() 716 { 717 $this 718 ->given( 719 $stream = new \Mock\Hoa\Stream\Test\Unit\SUTWithPublicClose(__FILE__), 720 $this->calling($stream)->_close = function () use (&$called) { 721 $called = true; 722 } 723 ) 724 ->when($result = $stream->__destruct()) 725 ->then 726 ->boolean($called) 727 ->isTrue(); 728 } 729 730 public function case_destruct_a_deferred_stream() 731 { 732 $this 733 ->given( 734 $stream = new \Mock\Hoa\Stream\Test\Unit\SUTWithPublicClose(__FILE__, null, true), 735 $this->calling($stream)->_close = function () use (&$called) { 736 $called = true; 737 } 738 ) 739 ->when($result = $stream->__destruct()) 740 ->then 741 ->variable($called) 742 ->isNull(); 743 } 744 745 public function case_protocol_reach_id() 746 { 747 $this 748 ->given( 749 $name = 'hoa://Test/Vfs/Foo?type=file', 750 $stream = new SUT($name) 751 ) 752 ->when($result = resolve('hoa://Library/Stream#' . $name)) 753 ->then 754 ->object($result) 755 ->isIdenticalTo($stream); 756 } 757 758 public function case_protocol_reach_unknown_id() 759 { 760 $this 761 ->given($name = 'hoa://Test/Vfs/Foo?type=file') 762 ->when($result = resolve('hoa://Library/Stream#' . $name)) 763 ->then 764 ->variable($result) 765 ->isNull(); 766 } 767} 768 769class SUT extends LUT\Stream 770{ 771 protected function &_open($streamName, LUT\Context $context = null) 772 { 773 if (null === $context) { 774 $out = fopen($streamName, 'rb'); 775 } else { 776 $out = fopen($streamName, 'rb', false, $context->getContext()); 777 } 778 779 return $out; 780 } 781 782 protected function _close() 783 { 784 return fclose($this->getStream()); 785 } 786} 787 788class SUTWithPublicClose extends SUT 789{ 790 public function _close() 791 { 792 return parent::_close(); 793 } 794} 795