Skip to content

Commit b8872f4

Browse files
committed
added serialization helper and restructured tests
1 parent 9f34ecc commit b8872f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+486
-59
lines changed

.github/workflows/test.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717

1818
name: Run tests on PHP v${{ matrix.php-version }}
1919

20+
env:
21+
TASKMASTER_TEST_TIME_FACTOR: 100
22+
2023
steps:
2124
- name: Checkout code
2225
uses: actions/checkout@v3

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,25 @@ class SynchronizedFieldTask extends \Aternos\Taskmaster\Task\Task
209209

210210
The result of this task is `6` because the `counter` property is synchronized and increased on both sides.
211211

212+
### Serialization in other classes
213+
The [`OnParent`](src/Task/OnParent.php), [`OnChild`](src/Task/OnChild.php) and [`OnBoth`](src/Task/OnBoth.php)
214+
attributes are only available in your [`Task`](src/Task/Task.php) class. If other objects are serialized but
215+
contain properties that should not be serialized, you can use the
216+
[`SerializationTrait`](src/Communication/Serialization/SerializationTrait.php) in your class
217+
and then add the [`Serializable`](src/Communication/Serialization/Serializable.php) or [`NotSerializable`](src/Communication/Serialization/NotSerializable.php)
218+
attributes to your properties.
219+
220+
You can use the [`Serializable`](src/Communication/Serialization/Serializable.php) attribute to mark properties that should be serialized.
221+
When using only the [`Serializable`](src/Communication/Serialization/Serializable.php) attribute, all properties that are not marked with the
222+
[`Serializable`](src/Communication/Serialization/Serializable.php) attribute will be ignored.
223+
224+
You can use the [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute to mark properties that should not be serialized.
225+
When using only the [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute, all properties that are not marked with the
226+
[`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute will be serialized.
227+
228+
When using both attributes, all properties **must** be marked with either the [`Serializable`](src/Communication/Serialization/Serializable.php)
229+
or [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute, otherwise an exception will be thrown.
230+
212231
### Handling the result
213232

214233
The `Task::handleResult()` function is called when the task returns a value. It can be used to handle

phpunit.xml

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
colors="true"
66
testdox="true">
77
<testsuites>
8-
<testsuite name="environment">
9-
<directory>test/Environment/</directory>
8+
<testsuite name="integration">
9+
<directory>test/Integration/</directory>
10+
</testsuite>
11+
<testsuite name="unit">
12+
<directory>test/Unit/</directory>
1013
</testsuite>
1114
</testsuites>
1215
<source>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Communication\Serialization;
4+
5+
use Attribute;
6+
7+
/**
8+
* Attribute NotSerializable
9+
*
10+
* This attribute is used to mark properties that should not be serialized.
11+
* When using only the #[NotSerializable] attribute, all properties that are not marked with the
12+
* #[NotSerializable] attribute will be serialized.
13+
*
14+
* When using both attributes, all properties MUST be marked with either the #[Serializable] or #[NotSerializable].
15+
*
16+
* You can use the {@link SerializationTrait} to implement serialization with these attributes.
17+
*
18+
* @package Aternos\Taskmaster\Communication\Serialization
19+
*/
20+
#[Attribute(Attribute::TARGET_PROPERTY)]
21+
class NotSerializable
22+
{
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Communication\Serialization;
4+
5+
use Attribute;
6+
7+
/**
8+
* Attribute Serializable
9+
*
10+
* This attribute is used to mark properties that should be serialized.
11+
* When using only the #[Serializable] attribute, all properties that are not marked with the
12+
* #[Serializable] attribute will be ignored.
13+
*
14+
* When using both attributes, all properties MUST be marked with either the #[Serializable] or #[NotSerializable].
15+
*
16+
* You can use the {@link SerializationTrait} to implement serialization with these attributes.
17+
*
18+
* @package Aternos\Taskmaster\Communication\Serialization
19+
*/
20+
#[Attribute(Attribute::TARGET_PROPERTY)]
21+
class Serializable
22+
{
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Communication\Serialization;
4+
5+
use ReflectionClass;
6+
7+
/**
8+
* Trait SerializationTrait
9+
*
10+
* This trait is used to serialize objects using the #[Serializable] and #[NotSerializable] attributes.
11+
*
12+
* You can use the #[Serializable] attribute to mark properties that should be serialized.
13+
* When using only the #[Serializable] attribute, all properties that are not marked with the
14+
* #[Serializable] attribute will be ignored.
15+
*
16+
* You can use the #[NotSerializable] attribute to mark properties that should not be serialized.
17+
* When using only the #[NotSerializable] attribute, all properties that are not marked with the
18+
* #[NotSerializable] attribute will be serialized.
19+
*
20+
* When using both attributes, all properties MUST be marked with either the #[Serializable] or #[NotSerializable]
21+
* attribute, otherwise an exception will be thrown.
22+
*
23+
* @package Aternos\Taskmaster\Communication\Serialization
24+
*/
25+
trait SerializationTrait
26+
{
27+
public function __serialize(): array
28+
{
29+
$unknown = [];
30+
$serializable = [];
31+
$hasNotSerializable = false;
32+
$hasSerializable = false;
33+
$hasUnknown = false;
34+
foreach ((new ReflectionClass($this))->getProperties() as $property) {
35+
if ($property->isStatic()) {
36+
continue;
37+
}
38+
39+
if ($property->getAttributes(NotSerializable::class)) {
40+
$hasNotSerializable = true;
41+
continue;
42+
}
43+
44+
if ($property->getAttributes(Serializable::class)) {
45+
$hasSerializable = true;
46+
if ($property->isInitialized($this)) {
47+
$serializable[$property->getName()] = $property->getValue($this);
48+
}
49+
continue;
50+
}
51+
52+
$hasUnknown = true;
53+
if ($property->isInitialized($this)) {
54+
$unknown[$property->getName()] = $property->getValue($this);
55+
}
56+
}
57+
58+
if ($hasNotSerializable) {
59+
if (!$hasSerializable) {
60+
return $unknown;
61+
}
62+
if ($hasUnknown) {
63+
throw new \LogicException("Found unknown properties (" . implode(", ", array_keys($unknown)) . ") on object using both, #[Serializable] and #[NotSerializable] attributes.");
64+
}
65+
return $serializable;
66+
}
67+
68+
if ($hasSerializable) {
69+
return $serializable;
70+
}
71+
return $unknown;
72+
}
73+
}

test/Environment/AsyncWorkerTestCase.php renamed to test/Integration/AsyncWorkerTestCase.php

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Exception\PhpError;
66
use Aternos\Taskmaster\Exception\TaskTimeoutException;
77
use Aternos\Taskmaster\Taskmaster;
8-
use Aternos\Taskmaster\Test\Task\InterruptableSleepTask;
9-
use Aternos\Taskmaster\Test\Task\SleepTask;
10-
use Aternos\Taskmaster\Test\Task\WarningTask;
8+
use Aternos\Taskmaster\Test\Util\Task\EmptyTask;
9+
use Aternos\Taskmaster\Test\Util\Task\InterruptableSleepTask;
10+
use Aternos\Taskmaster\Test\Util\Task\SleepTask;
11+
use Aternos\Taskmaster\Test\Util\Task\WarningTask;
1112
use Aternos\Taskmaster\Worker\WorkerInterface;
1213

1314
abstract class AsyncWorkerTestCase extends WorkerTestCase
@@ -22,12 +23,13 @@ protected function createTaskmaster(): void
2223

2324
public function testMultipleTasksRunAtTheSameTime(): void
2425
{
26+
$time = 20_000 * $this->getTimeFactor();
2527
$start = microtime(true);
26-
$this->addTasks(new SleepTask(500000), 3);
28+
$this->addTasks(new SleepTask($time), 3);
2729
$this->taskmaster->wait();
2830
$end = microtime(true);
29-
$time = ($end - $start) * 1000;
30-
$this->assertLessThan(1499, $time);
31+
$time = ($end - $start) * 1_000_000;
32+
$this->assertLessThan($time * 3 - 1, $time);
3133
}
3234

3335
public function testHandleWarning(): void
@@ -46,8 +48,8 @@ public function testHandleWarning(): void
4648

4749
public function testDefaultTimeout(): void
4850
{
49-
$this->taskmaster->setDefaultTaskTimeout(0.005);
50-
$tasks = $this->addTasks(new InterruptableSleepTask(10000), 3);
51+
$this->taskmaster->setDefaultTaskTimeout(0.005 * $this->getTimeFactor());
52+
$tasks = $this->addTasks(new InterruptableSleepTask(10_000 * $this->getTimeFactor()), 3);
5153
$this->taskmaster->wait();
5254
foreach ($tasks as $task) {
5355
$this->assertInstanceOf(TaskTimeoutException::class, $task->getError());
@@ -56,15 +58,14 @@ public function testDefaultTimeout(): void
5658

5759
public function testRecoverAfterTimeout(): void
5860
{
59-
$this->taskmaster->setDefaultTaskTimeout(0.05);
60-
$this->addTasks(new InterruptableSleepTask(100000), 3);
61-
$this->addTasks(new InterruptableSleepTask(1000), 3);
61+
$this->taskmaster->setDefaultTaskTimeout(0.005 * $this->getTimeFactor());
62+
$this->addTasks(new InterruptableSleepTask(10_000 * $this->getTimeFactor()), 3);
63+
$this->addTasks(new EmptyTask(), 3);
6264

6365
$counter = 0;
6466
foreach ($this->taskmaster->waitAndHandleTasks() as $task) {
6567
$counter++;
66-
$this->assertInstanceOf(InterruptableSleepTask::class, $task);
67-
if ($task->microseconds === 100000) {
68+
if ($task instanceof InterruptableSleepTask) {
6869
$this->assertInstanceOf(TaskTimeoutException::class, $task->getError());
6970
} else {
7071
$this->assertNull($task->getError());

test/Environment/ExitableAsyncWorkerTestCase.php renamed to test/Integration/ExitableAsyncWorkerTestCase.php

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Exception\PhpFatalErrorException;
66
use Aternos\Taskmaster\Exception\WorkerFailedException;
7-
use Aternos\Taskmaster\Test\Task\AdditionTask;
8-
use Aternos\Taskmaster\Test\Task\EmptyTask;
9-
use Aternos\Taskmaster\Test\Task\ErrorTask;
10-
use Aternos\Taskmaster\Test\Task\ExitTask;
7+
use Aternos\Taskmaster\Test\Util\Task\AdditionTask;
8+
use Aternos\Taskmaster\Test\Util\Task\ErrorTask;
9+
use Aternos\Taskmaster\Test\Util\Task\ExitTask;
1110

1211
abstract class ExitableAsyncWorkerTestCase extends AsyncWorkerTestCase
1312
{

test/Environment/ForkWorkerTest.php renamed to test/Integration/ForkWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Environment\Fork\ForkWorker;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

test/Environment/ProcessWorkerTest.php renamed to test/Integration/ProcessWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Environment\Process\ProcessWorker;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

test/Environment/ProxiedForkWorkerTest.php renamed to test/Integration/ProxiedForkWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Proxy\ProcessProxy;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

test/Environment/ProxiedProcessWorkerTest.php renamed to test/Integration/ProxiedProcessWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Proxy\ProcessProxy;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

test/Environment/ProxiedThreadWorkerTest.php renamed to test/Integration/ProxiedThreadWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Proxy\ProcessProxy;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

test/Environment/ProxiedWorkerTestTrait.php renamed to test/Integration/ProxiedWorkerTestTrait.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Exception\WorkerFailedException;
66
use Aternos\Taskmaster\Task\TaskInterface;
77
use Aternos\Taskmaster\Taskmaster;
8-
use Aternos\Taskmaster\Test\Task\SleepStatusTask;
8+
use Aternos\Taskmaster\Test\Util\Task\SleepStatusTask;
99

1010
trait ProxiedWorkerTestTrait
1111
{
@@ -21,7 +21,7 @@ abstract protected function addTasks(TaskInterface $task, int $amount): array;
2121
public function testTasksFailOnProxyDeath(): void
2222
{
2323
/** @var SleepStatusTask $tasks */
24-
$tasks = $this->addTasks(new SleepStatusTask(100000), 3);
24+
$tasks = $this->addTasks(new SleepStatusTask(100_000 * $this->getTimeFactor()), 3);
2525
do {
2626
$this->taskmaster->update();
2727
$runningTasks = 0;
@@ -43,7 +43,7 @@ public function testTasksFailOnProxyDeath(): void
4343
public function testProxyRestartsAfterFail(): void
4444
{
4545
/** @var SleepStatusTask $tasks */
46-
$tasks = $this->addTasks(new SleepStatusTask(100000), 6);
46+
$tasks = $this->addTasks(new SleepStatusTask(100_000 * $this->getTimeFactor()), 6);
4747
do {
4848
$this->taskmaster->update();
4949
$runningTasks = 0;
@@ -70,4 +70,6 @@ public function testProxyRestartsAfterFail(): void
7070
abstract public static function assertNull(mixed $actual, string $message = ''): void;
7171

7272
abstract public static function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void;
73+
74+
abstract protected function getTimeFactor(): int;
7375
}

test/Environment/SyncWorkerTest.php renamed to test/Integration/SyncWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Environment\Sync\SyncWorker;
66
use Aternos\Taskmaster\Taskmaster;

test/Environment/ThreadWorkerTest.php renamed to test/Integration/ThreadWorkerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Aternos\Taskmaster\Test\Environment;
3+
namespace Aternos\Taskmaster\Test\Integration;
44

55
use Aternos\Taskmaster\Environment\Thread\ThreadWorker;
66
use Aternos\Taskmaster\Worker\WorkerInterface;

0 commit comments

Comments
 (0)