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); + } + } }