Skip to content

Commit 34d0458

Browse files
committed
Run tests on PHP 8.4 and update test environment
1 parent da2e173 commit 34d0458

File tree

4 files changed

+81
-47
lines changed

4 files changed

+81
-47
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ on:
77
jobs:
88
PHPUnit:
99
name: PHPUnit (PHP ${{ matrix.php }})
10-
runs-on: ubuntu-22.04
10+
runs-on: ubuntu-24.04
1111
strategy:
1212
matrix:
1313
php:
14+
- 8.4
1415
- 8.3
1516
- 8.2
1617
- 8.1
@@ -38,7 +39,7 @@ jobs:
3839

3940
PHPUnit-hhvm:
4041
name: PHPUnit (HHVM)
41-
runs-on: ubuntu-22.04
42+
runs-on: ubuntu-24.04
4243
continue-on-error: true
4344
steps:
4445
- uses: actions/checkout@v4

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
},
2323
"require": {
2424
"php": ">=5.3",
25-
"clue/multicast-react": "^1.0 || ^0.2",
25+
"clue/multicast-react": "1.x-dev#f1bd5df0309b9f8b3486b09bf37aedfb042addb6",
2626
"react/event-loop": "^1.2",
27-
"react/promise": "^2.0 || ^1.0"
27+
"react/promise": "^3.2 || ^2.7 || ^1.2.1"
2828
},
2929
"require-dev": {
3030
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36"

src/Client.php

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,15 @@ class Client
1818
/** @var MulticastFactory */
1919
private $multicast;
2020

21-
/**
22-
* This class takes an optional `LoopInterface|null $loop` parameter that can be used to
23-
* pass the event loop instance to use for this object. You can use a `null` value
24-
* here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
25-
* This value SHOULD NOT be given unless you're sure you want to explicitly use a
26-
* given event loop instance.
27-
*
28-
* @param ?LoopInterface $loop
29-
* @param ?MulticastFactory $multicast
30-
*/
31-
public function __construct(LoopInterface $loop = null, MulticastFactory $multicast = null)
21+
public function __construct($loop = null, $multicast = null)
3222
{
23+
if ($loop !== null && !$loop instanceof LoopInterface) {
24+
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
25+
}
26+
if ($multicast !== null && !$multicast instanceof MulticastFactory) {
27+
throw new \InvalidArgumentException('Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory');
28+
}
29+
3330
$this->loop = $loop ?: Loop::get();
3431
$this->multicast = $multicast ?: new MulticastFactory($this->loop);
3532
}
@@ -46,29 +43,32 @@ public function search($searchTarget = 'ssdp:all', $mx = 2)
4643
$socket = $this->multicast->createSender();
4744
// TODO: The TTL for the IP packet SHOULD default to 2 and SHOULD be configurable.
4845

49-
$timer = $this->loop->addTimer($mx, function() use ($socket, &$deferred) {
50-
$deferred->resolve();
51-
$socket->close();
52-
});
46+
$messages = array();
5347

54-
$loop = $this->loop;
55-
$deferred = new Deferred(function () use ($socket, &$timer, $loop) {
56-
// canceling resulting promise cancels timer and closes socket
57-
$loop->cancelTimer($timer);
48+
$timer = $this->loop->addTimer($mx, function() use ($socket, &$deferred, &$messages) {
49+
// resolve promise with all collected messages
50+
$deferred->resolve($messages);
5851
$socket->close();
59-
throw new RuntimeException('Cancelled');
6052
});
6153

62-
$that = $this;
63-
$socket->on('message', function ($data, $remote) use ($deferred, $that) {
64-
$message = $that->parseMessage($data, $remote);
54+
$loop = $this->loop;
55+
$deferred = new Deferred(function () use ($socket, &$timer, $loop) {
56+
// canceling resulting promise cancels timer and closes socket
57+
$loop->cancelTimer($timer);
58+
$socket->close();
59+
throw new RuntimeException('Cancelled');
60+
});
6561

66-
$deferred->progress($message);
67-
});
62+
$that = $this;
63+
$socket->on('message', function ($data, $remote) use ($that, &$messages) {
64+
$message = $that->parseMessage($data, $remote);
65+
66+
$messages[] = $message;
67+
});
6868

69-
$socket->send($data, self::ADDRESS);
69+
$socket->send($data, self::ADDRESS);
7070

71-
return $deferred->promise();
71+
return $deferred->promise();
7272
}
7373

7474
/** @internal */
@@ -81,4 +81,4 @@ public function parseMessage($message, $remote)
8181
);
8282
return $message;
8383
}
84-
}
84+
}

tests/ClientTest.php

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,26 @@ public function testSearchCancel()
3939
// prefer newer EventLoop 1.0/0.5+ TimerInterface or fall back to legacy namespace
4040
$timer = $this->getMockBuilder(
4141
interface_exists('React\EventLoop\TimerInterface') ? 'React\EventLoop\TimerInterface' : 'React\EventLoop\Timer\TimerInterface'
42-
)->getMock();
42+
)->getMock();
4343

44-
$loop->expects($this->once())->method('addTimer')->willReturn($timer);
45-
$loop->expects($this->once())->method('cancelTimer')->with($timer);
44+
$loop->expects($this->once())->method('addTimer')->willReturn($timer);
45+
$loop->expects($this->once())->method('cancelTimer')->with($timer);
4646

47-
$multicast->expects($this->once())->method('createSender')->will($this->returnValue($socket));
47+
$multicast->expects($this->once())->method('createSender')->will($this->returnValue($socket));
4848

49-
$promise = $client->search();
49+
$promise = $client->search();
5050

51-
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
51+
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
5252

53-
if (!($promise instanceof \React\Promise\CancellablePromiseInterface)) {
54-
$this->markTestSkipped();
55-
}
53+
if (!($promise instanceof \React\Promise\CancellablePromiseInterface)) {
54+
$this->markTestSkipped();
55+
}
5656

57-
$socket->expects($this->once())->method('close');
57+
$socket->expects($this->once())->method('close');
5858

59-
$promise->cancel();
59+
$promise->cancel();
6060

61-
$promise->then(null, $this->expectCallableOnce());
61+
$promise->then(null, $this->expectCallableOnce());
6262
}
6363

6464
public function testSearchTimeout()
@@ -67,8 +67,41 @@ public function testSearchTimeout()
6767

6868
$promise = $client->search('ssdp:all', 0.01);
6969

70-
Loop::run();
70+
$result = null;
71+
$promise->then(function ($value) use (&$result) {
72+
$result = $value;
73+
});
74+
75+
Loop::run();
76+
77+
$this->assertTrue(is_array($result), 'Expected result to be an array');
78+
}
79+
80+
81+
public function testCtorThrowsForInvalidLoop()
82+
{
83+
if (method_exists($this, 'expectException')) {
84+
// PHPUnit 5.2+
85+
$this->expectException('InvalidArgumentException');
86+
$this->expectExceptionMessage('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
87+
} else {
88+
// legacy PHPUnit
89+
$this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
90+
}
91+
92+
new Client('invalid-loop');
93+
}
7194

72-
$promise->then($this->expectCallableOnce(), $this->expectCallableNever(), $this->expectCallableNever());
95+
public function testCtorThrowsForInvalidMulticast()
96+
{
97+
if (method_exists($this, 'expectException')) {
98+
$this->expectException('InvalidArgumentException');
99+
$this->expectExceptionMessage('Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory');
100+
} else {
101+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($multicast) expected null|Clue\React\Multicast\Factory');
102+
}
103+
104+
new Client(null, 'invalid-multicast');
73105
}
74-
}
106+
107+
}

0 commit comments

Comments
 (0)