diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d4f9a74..9cf4224 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,6 +17,9 @@ jobs: name: Run tests on PHP v${{ matrix.php-version }} + env: + TASKMASTER_TEST_TIME_FACTOR: 100 + steps: - name: Checkout code uses: actions/checkout@v3 diff --git a/README.md b/README.md index fbb2ac7..f320263 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,25 @@ class SynchronizedFieldTask extends \Aternos\Taskmaster\Task\Task The result of this task is `6` because the `counter` property is synchronized and increased on both sides. +### Serialization in other classes +The [`OnParent`](src/Task/OnParent.php), [`OnChild`](src/Task/OnChild.php) and [`OnBoth`](src/Task/OnBoth.php) +attributes are only available in your [`Task`](src/Task/Task.php) class. If other objects are serialized but +contain properties that should not be serialized, you can use the +[`SerializationTrait`](src/Communication/Serialization/SerializationTrait.php) in your class +and then add the [`Serializable`](src/Communication/Serialization/Serializable.php) or [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) +attributes to your properties. + +You can use the [`Serializable`](src/Communication/Serialization/Serializable.php) attribute to mark properties that should be serialized. +When using only the [`Serializable`](src/Communication/Serialization/Serializable.php) attribute, all properties that are not marked with the +[`Serializable`](src/Communication/Serialization/Serializable.php) attribute will be ignored. + +You can use the [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute to mark properties that should not be serialized. +When using only the [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute, all properties that are not marked with the +[`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute will be serialized. + +When using both attributes, all properties **must** be marked with either the [`Serializable`](src/Communication/Serialization/Serializable.php) +or [`NotSerializable`](src/Communication/Serialization/NotSerializable.php) attribute, otherwise an exception will be thrown. + ### Handling the result The `Task::handleResult()` function is called when the task returns a value. It can be used to handle diff --git a/phpunit.xml b/phpunit.xml index 8f1dc53..244de26 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -5,8 +5,11 @@ colors="true" testdox="true"> - - test/Environment/ + + test/Integration/ + + + test/Unit/ diff --git a/src/Communication/Serialization/NotSerializable.php b/src/Communication/Serialization/NotSerializable.php new file mode 100644 index 0000000..0d017e2 --- /dev/null +++ b/src/Communication/Serialization/NotSerializable.php @@ -0,0 +1,24 @@ +getProperties() as $property) { + if ($property->isStatic()) { + continue; + } + + if ($property->getAttributes(NotSerializable::class)) { + $hasNotSerializable = true; + continue; + } + + if ($property->getAttributes(Serializable::class)) { + $hasSerializable = true; + if ($property->isInitialized($this)) { + $serializable[$property->getName()] = $property->getValue($this); + } + continue; + } + + $hasUnknown = true; + if ($property->isInitialized($this)) { + $unknown[$property->getName()] = $property->getValue($this); + } + } + + if ($hasNotSerializable) { + if (!$hasSerializable) { + return $unknown; + } + if ($hasUnknown) { + throw new \LogicException("Found unknown properties (" . implode(", ", array_keys($unknown)) . ") on object using both, #[Serializable] and #[NotSerializable] attributes."); + } + return $serializable; + } + + if ($hasSerializable) { + return $serializable; + } + return $unknown; + } +} \ No newline at end of file diff --git a/test/Environment/AsyncWorkerTestCase.php b/test/Integration/AsyncWorkerTestCase.php similarity index 67% rename from test/Environment/AsyncWorkerTestCase.php rename to test/Integration/AsyncWorkerTestCase.php index abcd7fc..7599663 100644 --- a/test/Environment/AsyncWorkerTestCase.php +++ b/test/Integration/AsyncWorkerTestCase.php @@ -1,13 +1,14 @@ getTimeFactor(); $start = microtime(true); - $this->addTasks(new SleepTask(500000), 3); + $this->addTasks(new SleepTask($time), 3); $this->taskmaster->wait(); $end = microtime(true); - $time = ($end - $start) * 1000; - $this->assertLessThan(1499, $time); + $time = ($end - $start) * 1_000_000; + $this->assertLessThan($time * 3 - 1, $time); } public function testHandleWarning(): void @@ -46,8 +48,8 @@ public function testHandleWarning(): void public function testDefaultTimeout(): void { - $this->taskmaster->setDefaultTaskTimeout(0.005); - $tasks = $this->addTasks(new InterruptableSleepTask(10000), 3); + $this->taskmaster->setDefaultTaskTimeout(0.005 * $this->getTimeFactor()); + $tasks = $this->addTasks(new InterruptableSleepTask(10_000 * $this->getTimeFactor()), 3); $this->taskmaster->wait(); foreach ($tasks as $task) { $this->assertInstanceOf(TaskTimeoutException::class, $task->getError()); @@ -56,15 +58,14 @@ public function testDefaultTimeout(): void public function testRecoverAfterTimeout(): void { - $this->taskmaster->setDefaultTaskTimeout(0.05); - $this->addTasks(new InterruptableSleepTask(100000), 3); - $this->addTasks(new InterruptableSleepTask(1000), 3); + $this->taskmaster->setDefaultTaskTimeout(0.005 * $this->getTimeFactor()); + $this->addTasks(new InterruptableSleepTask(10_000 * $this->getTimeFactor()), 3); + $this->addTasks(new EmptyTask(), 3); $counter = 0; foreach ($this->taskmaster->waitAndHandleTasks() as $task) { $counter++; - $this->assertInstanceOf(InterruptableSleepTask::class, $task); - if ($task->microseconds === 100000) { + if ($task instanceof InterruptableSleepTask) { $this->assertInstanceOf(TaskTimeoutException::class, $task->getError()); } else { $this->assertNull($task->getError()); diff --git a/test/Environment/ExitableAsyncWorkerTestCase.php b/test/Integration/ExitableAsyncWorkerTestCase.php similarity index 90% rename from test/Environment/ExitableAsyncWorkerTestCase.php rename to test/Integration/ExitableAsyncWorkerTestCase.php index a086415..5f24ea7 100644 --- a/test/Environment/ExitableAsyncWorkerTestCase.php +++ b/test/Integration/ExitableAsyncWorkerTestCase.php @@ -1,13 +1,12 @@ addTasks(new SleepStatusTask(100000), 3); + $tasks = $this->addTasks(new SleepStatusTask(100_000 * $this->getTimeFactor()), 3); do { $this->taskmaster->update(); $runningTasks = 0; @@ -43,7 +43,7 @@ public function testTasksFailOnProxyDeath(): void public function testProxyRestartsAfterFail(): void { /** @var SleepStatusTask $tasks */ - $tasks = $this->addTasks(new SleepStatusTask(100000), 6); + $tasks = $this->addTasks(new SleepStatusTask(100_000 * $this->getTimeFactor()), 6); do { $this->taskmaster->update(); $runningTasks = 0; @@ -70,4 +70,6 @@ public function testProxyRestartsAfterFail(): void abstract public static function assertNull(mixed $actual, string $message = ''): void; abstract public static function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void; + + abstract protected function getTimeFactor(): int; } \ No newline at end of file diff --git a/test/Environment/SyncWorkerTest.php b/test/Integration/SyncWorkerTest.php similarity index 86% rename from test/Environment/SyncWorkerTest.php rename to test/Integration/SyncWorkerTest.php index 56d7f27..fd6194f 100644 --- a/test/Environment/SyncWorkerTest.php +++ b/test/Integration/SyncWorkerTest.php @@ -1,6 +1,6 @@ taskmaster->runTask(new EmptyTask()); diff --git a/test/Unit/Communication/Serialization/SerializationTest.php b/test/Unit/Communication/Serialization/SerializationTest.php new file mode 100644 index 0000000..b097c72 --- /dev/null +++ b/test/Unit/Communication/Serialization/SerializationTest.php @@ -0,0 +1,110 @@ +assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testWithoutAttributesUninitialized(): void + { + $object = new WithoutAttributesUninitialized(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testOnlySerializable(): void + { + $object = new OnlySerializable(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testOnlySerializableUninitialized(): void + { + $object = new OnlySerializableUninitialized(); + $this->assertEquals([], $object->__serialize()); + } + + public function testOnlyNotSerializable(): void + { + $object = new OnlyNotSerializable(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testOnlyNotSerializableUninitialized(): void + { + $object = new OnlyNotSerializableUninitialized(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testBothAttributes(): void + { + $object = new BothAttributes(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testBothAttributesUninitialized(): void + { + $object = new BothAttributesUninitialized(); + $this->assertEquals([ + "public" => 1, + "protected" => 2, + "private" => 3 + ], $object->__serialize()); + } + + public function testBothAttributesAndUnknown(): void + { + $object = new BothAttributesAndUnknown(); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage("Found unknown properties (unknown) on object using both, #[Serializable] and #[NotSerializable] attributes."); + $object->__serialize(); + } + + public function testBothAttributesAndUnknownUninitialized(): void + { + $object = new BothAttributesAndUnknownUninitialized(); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage("Found unknown properties (unknown) on object using both, #[Serializable] and #[NotSerializable] attributes."); + $object->__serialize(); + } +} \ No newline at end of file diff --git a/test/Util/Serialization/BothAttributes.php b/test/Util/Serialization/BothAttributes.php new file mode 100644 index 0000000..a481689 --- /dev/null +++ b/test/Util/Serialization/BothAttributes.php @@ -0,0 +1,16 @@ +