diff --git a/.gitattributes b/.gitattributes
index aa7854a..da20d18 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,4 +3,5 @@
/.gitignore export-ignore
/examples/ export-ignore
/phpunit.xml.dist export-ignore
+/phpunit.xml.legacy export-ignore
/tests/ export-ignore
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1d0a30d..0c5d3e8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,6 +13,7 @@ jobs:
strategy:
matrix:
php:
+ - 8.0
- 7.4
- 7.3
- 7.2
@@ -34,6 +35,9 @@ jobs:
- run: sudo /etc/init.d/quasselcore status || sudo /etc/init.d/quasselcore start
- run: sudo /etc/init.d/quasselcore status || sleep 2
- run: vendor/bin/phpunit --coverage-text
+ if: ${{ matrix.php >= 7.3 }}
+ - run: vendor/bin/phpunit --coverage-text -c phpunit.xml.legacy
+ if: ${{ matrix.php < 7.3 }}
PHPUnit-hhvm:
name: PHPUnit (HHVM)
@@ -45,7 +49,6 @@ jobs:
version: lts-3.30
- run: sudo apt-get -qq update || true # update package list and ignore temporary network errors
- run: sudo apt-get --no-install-recommends -qq install -y quassel-core
- - run: hhvm $(which composer) require phpunit/phpunit:^5 --dev --no-interaction # requires legacy phpunit
- run: hhvm $(which composer) install
- run: sudo /etc/init.d/quasselcore status || sudo /etc/init.d/quasselcore start
- run: sudo /etc/init.d/quasselcore status || sleep 2
diff --git a/README.md b/README.md
index bbbc152..c19fbb1 100644
--- a/README.md
+++ b/README.md
@@ -250,7 +250,7 @@ $ composer require clue/quassel-react:^0.6
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
This project aims to run on any platform and thus does not require any PHP
-extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
+extensions and supports running on legacy PHP 5.3 through current PHP 8+ and
HHVM.
It's *highly recommended to use PHP 7+* for this project.
diff --git a/composer.json b/composer.json
index 95d08b9..2ad49ae 100644
--- a/composer.json
+++ b/composer.json
@@ -26,6 +26,6 @@
},
"require-dev": {
"clue/block-react": "^1.1",
- "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ebc9db1..0e44cd7 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,14 +1,19 @@
-
+
+
-
+
./tests/
-
-
+
+
./src/
-
-
-
\ No newline at end of file
+
+
+
diff --git a/phpunit.xml.legacy b/phpunit.xml.legacy
new file mode 100644
index 0000000..a621b40
--- /dev/null
+++ b/phpunit.xml.legacy
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ ./tests/
+
+
+
+
+ ./src/
+
+
+
diff --git a/tests/ClientTest.php b/tests/ClientTest.php
index de789ca..fd1790f 100644
--- a/tests/ClientTest.php
+++ b/tests/ClientTest.php
@@ -10,7 +10,15 @@
class ClientTest extends TestCase
{
- public function setUp()
+ private $stream;
+ private $protocol;
+ private $splitter;
+ private $client;
+
+ /**
+ * @before
+ */
+ public function setUpClient()
{
$this->stream = $this->getMockBuilder('React\Stream\DuplexStreamInterface')->getMock();
$this->protocol = $this->getMockBuilder('Clue\React\Quassel\Io\Protocol')->disableOriginalConstructor()->getMock();
@@ -243,7 +251,7 @@ public function testWriteHeartBeatRequestWithoutTimestampSendsCurrentTimestamp()
$that->assertEquals(Protocol::REQUEST_HEARTBEAT, $value[0]);
$that->assertInstanceOf('DateTime', $value[1]);
- $that->assertEquals(microtime(true), $value[1]->getTimestamp(), '', 2);
+ $that->assertEqualsDelta(microtime(true), $value[1]->getTimestamp(), 2);
return true;
}));
$this->splitter->expects($this->once())->method('writePacket');
diff --git a/tests/FactoryIntegrationTest.php b/tests/FactoryIntegrationTest.php
index 1bc1c89..7f32f78 100644
--- a/tests/FactoryIntegrationTest.php
+++ b/tests/FactoryIntegrationTest.php
@@ -86,9 +86,6 @@ public function testCreateClientCreatesSecondConnectionWithoutProbeIfConnectionC
Block\sleep(0.1, $loop);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientRejectsIfServerRespondsWithInvalidData()
{
$loop = LoopFactory::create();
@@ -104,6 +101,7 @@ public function testCreateClientRejectsIfServerRespondsWithInvalidData()
$factory = new Factory($loop);
$promise = $factory->createClient($uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
@@ -132,9 +130,6 @@ public function testCreateClientWithAuthSendsClientInitAfterProbe()
Block\sleep(0.1, $loop);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithAuthRejectsIfServerClosesAfterClientInit()
{
$loop = LoopFactory::create();
@@ -153,12 +148,10 @@ public function testCreateClientWithAuthRejectsIfServerClosesAfterClientInit()
$factory = new Factory($loop);
$promise = $factory->createClient('user:pass@' . $uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithAuthRejectsIfServerSendsClientInitRejectAfterClientInit()
{
$loop = LoopFactory::create();
@@ -182,12 +175,10 @@ public function testCreateClientWithAuthRejectsIfServerSendsClientInitRejectAfte
$factory = new Factory($loop);
$promise = $factory->createClient('user:pass@' . $uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithAuthRejectsIfServerSendsUnknownMessageAfterClientInit()
{
$loop = LoopFactory::create();
@@ -211,12 +202,10 @@ public function testCreateClientWithAuthRejectsIfServerSendsUnknownMessageAfterC
$factory = new Factory($loop);
$promise = $factory->createClient('user:pass@' . $uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithAuthRejectsIfServerSendsInvalidTruncatedResponseAfterClientInit()
{
$loop = LoopFactory::create();
@@ -235,12 +224,10 @@ public function testCreateClientWithAuthRejectsIfServerSendsInvalidTruncatedResp
$factory = new Factory($loop);
$promise = $factory->createClient('user:pass@' . $uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithAuthRejectsIfServerSendsClientInitAckNotConfigured()
{
$loop = LoopFactory::create();
@@ -263,6 +250,7 @@ public function testCreateClientWithAuthRejectsIfServerSendsClientInitAckNotConf
$factory = new Factory($loop);
$promise = $factory->createClient('user:pass@' . $uri);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, $loop, 10.0);
}
diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php
index 8ede72f..7bb43d0 100644
--- a/tests/FactoryTest.php
+++ b/tests/FactoryTest.php
@@ -9,7 +9,15 @@
class FactoryTest extends TestCase
{
- public function setUp()
+ private $loop;
+ private $connector;
+ private $prober;
+ private $factory;
+
+ /**
+ * @before
+ */
+ public function setUpFactory()
{
$this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php
index decefb9..43f89a8 100644
--- a/tests/FunctionalTest.php
+++ b/tests/FunctionalTest.php
@@ -20,7 +20,10 @@ class FunctionalTest extends TestCase
private static $loop;
private static $blocker;
- public static function setUpBeforeClass()
+ /**
+ * @beforeClass
+ */
+ public static function setUpEnvironmentAndLoop()
{
if (!getenv('QUASSEL_HOST')) {
return;
@@ -40,7 +43,10 @@ public static function setUpBeforeClass()
self::$loop = LoopFactory::create();
}
- public function setUp()
+ /**
+ * @before
+ */
+ public function setUpSkipOnMissingEnvironment()
{
if (!self::$host) {
$this->markTestSkipped('No ENV QUASSEL_HOST (plus optionally QUASSEL_USER and QUASSEL_PASS) given');
@@ -170,7 +176,7 @@ public function testWriteHeartBeatDefaultsToCurrentTime(Client $client)
$received = Block\await($promise, self::$loop, 10.0);
$this->assertTrue($received instanceof \DateTime);
- $this->assertEquals(microtime(true), $received->getTimestamp(), '', 2.0);
+ $this->assertEqualsDelta(microtime(true), $received->getTimestamp(), 2.0);
}
/**
@@ -274,9 +280,6 @@ public function testRequestBacklogReceivesBacklog()
$client->close();
}
- /**
- * @expectedException RuntimeException
- */
public function testCreateClientWithInvalidAuthUrlRejects()
{
$factory = new Factory(self::$loop);
@@ -284,6 +287,7 @@ public function testCreateClientWithInvalidAuthUrlRejects()
$url = rawurlencode(self::$username) . ':@' . self::$host;
$promise = $factory->createClient($url);
+ $this->setExpectedException('RuntimeException');
Block\await($promise, self::$loop, 10.0);
}
diff --git a/tests/Io/DatastreamProtocolTest.php b/tests/Io/DatastreamProtocolTest.php
index 4b277c0..0260e35 100644
--- a/tests/Io/DatastreamProtocolTest.php
+++ b/tests/Io/DatastreamProtocolTest.php
@@ -9,7 +9,10 @@
class DatastreamProtocolTest extends AbstractProtocolTest
{
- public function setUp()
+ /**
+ * @before
+ */
+ public function setUpProtocol()
{
$this->protocol = Protocol::createFromProbe(Protocol::TYPE_DATASTREAM);
}
@@ -19,11 +22,9 @@ public function testIsNotLegacy()
$this->assertFalse($this->protocol->isLegacy());
}
- /**
- * @expectedException InvalidArgumentException
- */
public function testCanNotTransportListStartingWithString()
{
+ $this->setExpectedException('InvalidArgumentException');
$this->protocol->serializeVariantPacket(array('does', 'not', 'work'));
}
diff --git a/tests/Io/LegacyProtocolTest.php b/tests/Io/LegacyProtocolTest.php
index 779fb95..720acbf 100644
--- a/tests/Io/LegacyProtocolTest.php
+++ b/tests/Io/LegacyProtocolTest.php
@@ -9,7 +9,10 @@
class LegacyProtocolTest extends AbstractProtocolTest
{
- public function setUp()
+ /**
+ * @before
+ */
+ public function setUpProtocol()
{
$this->protocol = Protocol::createFromProbe(Protocol::TYPE_LEGACY);
}
diff --git a/tests/Io/PacketSplitterTest.php b/tests/Io/PacketSplitterTest.php
index b843e86..1f75b05 100644
--- a/tests/Io/PacketSplitterTest.php
+++ b/tests/Io/PacketSplitterTest.php
@@ -9,7 +9,10 @@ class PacketSplitterTest extends TestCase
{
private $splitter;
- public function setUp()
+ /**
+ * @before
+ */
+ public function setUpSplitter()
{
$this->splitter = new PacketSplitter();
}
@@ -29,11 +32,9 @@ public function testWriteCompletePacketToSplitterWillEmitImmediately()
$this->splitter->push($packet, $this->expectCallableOnce());
}
- /**
- * @expectedException OverflowException
- */
public function testWillThrowForHugePacket()
{
+ $this->setExpectedException('OverflowException');
$this->splitter->push("\xFF\xFF\xFF\xFF", $this->expectCallableNever());
}
}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index e1998aa..3286b46 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -59,4 +59,32 @@ protected function expectPromiseReject($promise)
return $promise;
}
+
+ public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null)
+ {
+ if (method_exists($this, 'expectException')) {
+ // PHPUnit 6+
+ $this->expectException($exception);
+ if ($exceptionMessage !== '') {
+ $this->expectExceptionMessage($exceptionMessage);
+ }
+ if ($exceptionCode !== null) {
+ $this->expectExceptionCode($exceptionCode);
+ }
+ } else {
+ // legacy PHPUnit 4 - PHPUnit 5
+ parent::setExpectedException($exception, $exceptionMessage, $exceptionCode);
+ }
+ }
+
+ public function assertEqualsDelta($expected, $actual, $delta, $message = '')
+ {
+ if (method_exists($this, 'assertEqualsWithDelta')) {
+ // PHPUnit 7.5+
+ $this->assertEqualsWithDelta($expected, $actual, $delta, $message);
+ } else {
+ // legacy PHPUnit 4 - PHPUnit 7.4
+ $this->assertEquals($expected, $actual, $message, $delta);
+ }
+ }
}