Skip to content

Commit d707ff9

Browse files
authored
Merge pull request #904 from jhdxr/feature/tests
Add test
2 parents 3386ef9 + 098c8b6 commit d707ff9

13 files changed

+465
-0
lines changed

.github/workflows/test.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: tests
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- feature/tests
8+
pull_request:
9+
schedule:
10+
- cron: '0 0 * * *'
11+
12+
jobs:
13+
linux_tests:
14+
runs-on: ubuntu-22.04
15+
16+
strategy:
17+
fail-fast: true
18+
matrix:
19+
php: [8.1, 8.2]
20+
stability: [prefer-lowest, prefer-stable]
21+
22+
name: PHP ${{ matrix.php }} - ${{ matrix.stability }}
23+
24+
steps:
25+
- name: Checkout code
26+
uses: actions/checkout@v3
27+
28+
- name: Setup PHP
29+
uses: shivammathur/setup-php@v2
30+
with:
31+
php-version: ${{ matrix.php }}
32+
extensions: json
33+
ini-values: error_reporting=E_ALL
34+
tools: composer:v2
35+
coverage: xdebug
36+
37+
- name: Install dependencies
38+
uses: nick-fields/retry@v2
39+
with:
40+
timeout_minutes: 5
41+
max_attempts: 5
42+
command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress
43+
# command: composer install --prefer-dist --no-interaction --no-progress
44+
45+
- name: Execute tests
46+
run: vendor/bin/pest --coverage
47+

composer.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,14 @@
3838
"minimum-stability": "dev",
3939
"conflict": {
4040
"ext-swow": "<v1.0.0"
41+
},
42+
"require-dev": {
43+
"pestphp/pest": "2.x-dev",
44+
"mockery/mockery": "2.0.x-dev"
45+
},
46+
"config": {
47+
"allow-plugins": {
48+
"pestphp/pest-plugin": true
49+
}
4150
}
4251
}

phpunit.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
>
7+
<testsuites>
8+
<testsuite name="Test Suite">
9+
<directory suffix="Test.php">./tests</directory>
10+
</testsuite>
11+
</testsuites>
12+
<coverage/>
13+
<source>
14+
<include>
15+
<directory suffix=".php">./src</directory>
16+
</include>
17+
</source>
18+
</phpunit>

tests/Feature/ExampleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
test('example', function () {
4+
expect(true)->toBeTrue();
5+
// expect('3')->toBe(3);
6+
});

tests/Feature/UdpConnectionTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
//example from manual
3+
use Workerman\Connection\AsyncUdpConnection;
4+
use Workerman\Timer;
5+
use Workerman\Worker;
6+
7+
it('tests udp connection', function () {
8+
/** @noinspection PhpObjectFieldsAreOnlyWrittenInspection */
9+
$server = new Worker('udp://0.0.0.0:9292');
10+
$server->onMessage = function ($connection, $data) {
11+
expect($data)->toBe('hello');
12+
$connection->send('xiami');
13+
};
14+
$server->onWorkerStart = function () {
15+
//client
16+
Timer::add(1, function () {
17+
$client = new AsyncUdpConnection('udp://127.0.0.1:1234');
18+
$client->onConnect = function ($client) {
19+
$client->send('hello');
20+
};
21+
$client->onMessage = function ($client, $data) {
22+
expect($data)->toBe('xiami');
23+
//terminal this test
24+
terminate_current_test();
25+
};
26+
$client->connect();
27+
}, null, false);
28+
};
29+
Worker::runAll();
30+
})->skipOnWindows() //require posix, multiple workers
31+
->skip(message: 'this test needs to run isolated process while pest not support doing so yet');

tests/Pest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
|--------------------------------------------------------------------------
5+
| Test Case
6+
|--------------------------------------------------------------------------
7+
|
8+
| The closure you provide to your test functions is always bound to a specific PHPUnit test
9+
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
10+
| need to change it using the "uses()" function to bind a different classes or traits.
11+
|
12+
*/
13+
14+
// uses(Tests\TestCase::class)->in('Feature');
15+
16+
/*
17+
|--------------------------------------------------------------------------
18+
| Expectations
19+
|--------------------------------------------------------------------------
20+
|
21+
| When you're writing tests, you often need to check that values meet certain conditions. The
22+
| "expect()" function gives you access to a set of "expectations" methods that you can use
23+
| to assert different things. Of course, you may extend the Expectation API at any time.
24+
|
25+
*/
26+
27+
use Workerman\Connection\TcpConnection;
28+
29+
expect()->extend('toBeOne', function () {
30+
return $this->toBe(1);
31+
});
32+
33+
/*
34+
|--------------------------------------------------------------------------
35+
| Functions
36+
|--------------------------------------------------------------------------
37+
|
38+
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
39+
| project that you don't want to repeat in every file. Here you can also expose helpers as
40+
| global functions to help you to reduce the number of lines of code in your test files.
41+
|
42+
*/
43+
44+
function something()
45+
{
46+
// ..
47+
}
48+
49+
function testWithConnectionClose(Closure $closure, string $dataContains = null, $connectionClass = TcpConnection::class): void
50+
{
51+
$tcpConnection = Mockery::spy($connectionClass);
52+
$closure($tcpConnection);
53+
if ($dataContains) {
54+
$tcpConnection->shouldHaveReceived('close', function ($actual) use ($dataContains) {
55+
return str_contains($actual, $dataContains);
56+
});
57+
} else {
58+
$tcpConnection->shouldHaveReceived('close');
59+
}
60+
}
61+
62+
function terminate_current_test()
63+
{
64+
posix_kill(posix_getppid(), SIGINT);
65+
}

tests/TestCase.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use PHPUnit\Framework\TestCase as BaseTestCase;
6+
7+
abstract class TestCase extends BaseTestCase
8+
{
9+
//
10+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
use Workerman\Connection\UdpConnection;
4+
use Symfony\Component\Process\PhpProcess;
5+
6+
$remoteAddress = '[::1]:12345';
7+
$process = new PhpProcess(<<<PHP
8+
<?php
9+
\$socketServer = stream_socket_server("udp://$remoteAddress", \$errno, \$errstr, STREAM_SERVER_BIND);
10+
do{
11+
\$data = stream_socket_recvfrom(\$socketServer, 3);
12+
}while(\$data !== false && \$data !== 'bye');
13+
PHP
14+
);
15+
$process->start();
16+
17+
it('tests ' . UdpConnection::class, function () use ($remoteAddress) {
18+
19+
$socketClient = stream_socket_client("udp://$remoteAddress");
20+
$udpConnection = new UdpConnection($socketClient, $remoteAddress);
21+
$udpConnection->protocol = \Workerman\Protocols\Text::class;
22+
expect($udpConnection->send('foo'))->toBeTrue();
23+
24+
expect($udpConnection->getRemoteIp())->toBe('::1');
25+
expect($udpConnection->getRemotePort())->toBe(12345);
26+
expect($udpConnection->getRemoteAddress())->toBe($remoteAddress);
27+
expect($udpConnection->getLocalIp())->toBeIn(['::1', '[::1]', '127.0.0.1']);
28+
expect($udpConnection->getLocalPort())->toBeInt();
29+
30+
expect(json_encode($udpConnection))->toBeJson()
31+
->toContain('transport')
32+
->toContain('getRemoteIp')
33+
->toContain('remotePort')
34+
->toContain('getRemoteAddress')
35+
->toContain('getLocalIp')
36+
->toContain('getLocalPort')
37+
->toContain('isIpV4')
38+
->toContain('isIpV6');
39+
40+
$udpConnection->close('bye');
41+
if (is_resource($socketClient)) {
42+
fclose($socketClient);
43+
}
44+
});

tests/Unit/Protocols/FrameTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
use Workerman\Protocols\Frame;
4+
5+
it('tests ::input', function () {
6+
expect(Frame::input('foo'))->toBe(0);
7+
expect(Frame::input("\0\0\0*foobar"))
8+
->toBe(42);
9+
});
10+
11+
it('tests ::decode', function () {
12+
$buffer = pack('N', 5) . 'jhdxr';
13+
expect(Frame::decode($buffer))
14+
->toBe('jhdxr');
15+
});
16+
17+
it('tests ::encode', function () {
18+
expect(Frame::encode('jhdxr'))
19+
->toBe(pack('N', 9) . 'jhdxr');
20+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
use Workerman\Protocols\Http\Response;
4+
5+
it('test some simple case', function () {
6+
$response = new Response(201, ['X-foo' => 'bar'], 'hello, xiami');
7+
8+
expect($response->getStatusCode())->toBe(201);
9+
expect($response->getHeaders())->toBe(['X-foo' => 'bar']);
10+
expect($response->rawBody())->toBe('hello, xiami');
11+
12+
//headers
13+
$response->header('abc', '123');
14+
$response->withHeader('X-foo', 'baz');
15+
$response->withHeaders(['def' => '456']);
16+
expect((string)$response)
17+
->toContain('X-foo: baz')
18+
->toContain('abc: 123')
19+
->toContain('def: 456');
20+
$response->withoutHeader('def');
21+
expect((string)$response)->not->toContain('def: 456');
22+
expect($response->getHeader('abc'))
23+
->toBe('123');
24+
25+
$response->withStatus(202, 'some reason');
26+
expect($response->getReasonPhrase())->toBe('some reason');
27+
28+
$response->withProtocolVersion('1.0');
29+
$response->withBody('hello, world');
30+
expect((string)$response)
31+
->toContain('HTTP/1.0')
32+
->toContain('hello, world')
33+
->toContain('Content-Type: ')
34+
->toContain('Content-Length: 12')
35+
->not()->toContain('Transfer-Encoding: ');
36+
37+
38+
//cookie
39+
$response->cookie('foo', 'bar', httpOnly: true, domain: 'xia.moe');
40+
expect((string)$response)
41+
->toContain('Set-Cookie: foo=bar; Domain=xia.moe; HttpOnly');
42+
});
43+
44+
it('tests file', function (){
45+
//todo may have to redo the simple test,
46+
// as the implementation of headers is a different function for files.
47+
// or actually maybe the Response is the one should be rewritten to reuse?
48+
$response = new Response();
49+
$tmpFile = tempnam(sys_get_temp_dir(), 'test');
50+
rename($tmpFile, $tmpFile .'.jpg');
51+
$tmpFile .= '.jpg';
52+
file_put_contents($tmpFile, 'hello, xiami');
53+
$response->withFile($tmpFile, 0, 12);
54+
expect((string)$response)
55+
->toContain('Content-Type: image/jpeg')
56+
->toContain('Last-Modified: ');
57+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Workerman\Protocols\Http\ServerSentEvents;
4+
5+
it('tests ' . ServerSentEvents::class, function () {
6+
$data = [
7+
'event' => 'ping',
8+
'data' => 'some thing',
9+
'id' => 1000,
10+
'retry' => 5000,
11+
];
12+
$sse = new ServerSentEvents($data);
13+
$expected = "event: {$data['event']}\ndata: {$data['data']}\n\nid: {$data['id']}\nretry: {$data['retry']}\n";
14+
expect((string)$sse)->toBe($expected);
15+
});

0 commit comments

Comments
 (0)