LockerTest.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <?php
  2. declare(strict_types=1);
  3. namespace tests;
  4. use PHPUnit\Framework\TestCase;
  5. use Workerman\Coroutine\Locker;
  6. use RuntimeException;
  7. use Workerman\Coroutine;
  8. use ReflectionClass;
  9. use Workerman\Timer;
  10. class LockerTest extends TestCase
  11. {
  12. public function testLock()
  13. {
  14. $key = 'testLock';
  15. Locker::lock($key);
  16. $timeStart = microtime(true);
  17. $timeDiff2 = 0;
  18. Coroutine::create(function () use ($key, $timeStart, &$timeDiff2) {
  19. $this->assertChannelExists($key);
  20. Locker::lock($key);
  21. $timeDiff = microtime(true) - $timeStart;
  22. $this->assertGreaterThan($timeDiff2, $timeDiff);
  23. Locker::unlock($key);
  24. });
  25. usleep(100000);
  26. $timeDiff2 = microtime(true) - $timeStart;
  27. Locker::unlock($key);
  28. }
  29. public function testLockAndUnlock()
  30. {
  31. $key = 'testLockAndUnlock';
  32. $this->assertTrue(Locker::lock($key));
  33. $this->assertTrue(Locker::unlock($key));
  34. $this->assertChannelRemoved($key);
  35. }
  36. public function testUnlockWithoutLockThrowsException()
  37. {
  38. $this->expectException(RuntimeException::class);
  39. Locker::unlock('non_existent_key');
  40. }
  41. public function testRelockAfterUnlock()
  42. {
  43. $key = 'testRelockAfterUnlock';
  44. Locker::lock($key);
  45. Locker::unlock($key);
  46. $this->assertTrue(Locker::lock($key));
  47. Locker::unlock($key);
  48. $this->assertChannelRemoved($key);
  49. }
  50. public function testMultipleCoroutinesLocking()
  51. {
  52. $key = 'testMultipleCoroutinesLocking';
  53. $results = [];
  54. Coroutine::create(function () use ($key, &$results) {
  55. Coroutine::create(function () use ($key, &$results) {
  56. Locker::lock($key);
  57. $results[] = 'A';
  58. Timer::sleep(0.1);
  59. usleep(100000);
  60. Locker::unlock($key);
  61. });
  62. Coroutine::create(function () use ($key, &$results) {
  63. Timer::sleep(0.05);
  64. Locker::lock($key);
  65. $results[] = 'B';
  66. Locker::unlock($key);
  67. });
  68. Coroutine::create(function () use ($key, &$results) {
  69. Timer::sleep(0.05);
  70. Locker::lock($key);
  71. $results[] = 'C';
  72. Locker::unlock($key);
  73. });
  74. });
  75. Timer::sleep(0.3);
  76. $this->assertEquals(['A', 'B', 'C'], $results);
  77. $this->assertChannelRemoved($key);
  78. }
  79. public function testChannelRemainsWhenWaiting()
  80. {
  81. $key = 'testChannelRemainsWhenWaiting';
  82. Locker::lock($key);
  83. Coroutine::create(function () use ($key) {
  84. Coroutine::create(function () use ($key) {
  85. Locker::lock($key);
  86. Locker::unlock($key);
  87. });
  88. Locker::unlock($key);
  89. $this->assertChannelRemoved($key);
  90. });
  91. }
  92. private function assertChannelExists(string $key): void
  93. {
  94. $channels = $this->getChannels();
  95. $this->assertArrayHasKey($key, $channels, "Channel for key '$key' should exist");
  96. }
  97. private function assertChannelRemoved(string $key): void
  98. {
  99. $channels = $this->getChannels();
  100. $this->assertArrayNotHasKey($key, $channels, "Channel for key '$key' should be removed");
  101. }
  102. private function getChannels(): array
  103. {
  104. $reflector = new ReflectionClass(Locker::class);
  105. $property = $reflector->getProperty('channels');
  106. return $property->getValue();
  107. }
  108. }