From b8872f4890ad0661c1831db375d95bb10ef57b6f Mon Sep 17 00:00:00 2001 From: Matthias Neid Date: Thu, 26 Oct 2023 13:48:08 +0200 Subject: [PATCH] added serialization helper and restructured tests --- .github/workflows/test.yaml | 3 + README.md | 19 +++ phpunit.xml | 7 +- .../Serialization/NotSerializable.php | 24 ++++ .../Serialization/Serializable.php | 24 ++++ .../Serialization/SerializationTrait.php | 73 ++++++++++++ .../AsyncWorkerTestCase.php | 29 ++--- .../ExitableAsyncWorkerTestCase.php | 9 +- .../ForkWorkerTest.php | 2 +- .../ProcessWorkerTest.php | 2 +- .../ProxiedForkWorkerTest.php | 2 +- .../ProxiedProcessWorkerTest.php | 2 +- .../ProxiedThreadWorkerTest.php | 2 +- .../ProxiedWorkerTestTrait.php | 10 +- .../SyncWorkerTest.php | 2 +- .../ThreadWorkerTest.php | 2 +- .../WorkerTestCase.php | 30 +++-- .../Serialization/SerializationTest.php | 110 ++++++++++++++++++ test/Util/Serialization/BothAttributes.php | 16 +++ .../BothAttributesAndUnknown.php | 18 +++ .../BothAttributesAndUnknownUninitialized.php | 18 +++ .../BothAttributesUninitialized.php | 16 +++ .../Serialization/OnlyNotSerializable.php | 15 +++ .../OnlyNotSerializableUninitialized.php | 15 +++ test/Util/Serialization/OnlySerializable.php | 15 +++ .../OnlySerializableUninitialized.php | 15 +++ .../Serialization/SerializationExample.php | 10 ++ test/Util/Serialization/WithoutAttributes.php | 10 ++ .../WithoutAttributesUninitialized.php | 13 +++ test/{ => Util}/Task/AdditionTask.php | 2 +- test/{ => Util}/Task/CallbackTask.php | 2 +- test/{ => Util}/Task/ChildExceptionTask.php | 2 +- test/{ => Util}/Task/EmptyTask.php | 2 +- test/{ => Util}/Task/ErrorTask.php | 2 +- test/{ => Util}/Task/ExitTask.php | 3 +- .../Task/InterruptableSleepTask.php | 3 +- test/{ => Util}/Task/ParentExceptionTask.php | 2 +- test/{ => Util}/Task/SleepCountTask.php | 2 +- test/{ => Util}/Task/SleepStatusTask.php | 2 +- test/{ => Util}/Task/SleepTask.php | 2 +- .../Task/SuppressedErrorOutputTask.php | 2 +- .../{ => Util}/Task/SynchronizedFieldTask.php | 2 +- .../Task/UnsynchronizedFieldTask.php | 2 +- test/{ => Util}/Task/WarningTask.php | 2 +- 44 files changed, 486 insertions(+), 59 deletions(-) create mode 100644 src/Communication/Serialization/NotSerializable.php create mode 100644 src/Communication/Serialization/Serializable.php create mode 100644 src/Communication/Serialization/SerializationTrait.php rename test/{Environment => Integration}/AsyncWorkerTestCase.php (67%) rename test/{Environment => Integration}/ExitableAsyncWorkerTestCase.php (90%) rename test/{Environment => Integration}/ForkWorkerTest.php (88%) rename test/{Environment => Integration}/ProcessWorkerTest.php (85%) rename test/{Environment => Integration}/ProxiedForkWorkerTest.php (89%) rename test/{Environment => Integration}/ProxiedProcessWorkerTest.php (87%) rename test/{Environment => Integration}/ProxiedThreadWorkerTest.php (89%) rename test/{Environment => Integration}/ProxiedWorkerTestTrait.php (85%) rename test/{Environment => Integration}/SyncWorkerTest.php (86%) rename test/{Environment => Integration}/ThreadWorkerTest.php (88%) rename test/{Environment => Integration}/WorkerTestCase.php (88%) create mode 100644 test/Unit/Communication/Serialization/SerializationTest.php create mode 100644 test/Util/Serialization/BothAttributes.php create mode 100644 test/Util/Serialization/BothAttributesAndUnknown.php create mode 100644 test/Util/Serialization/BothAttributesAndUnknownUninitialized.php create mode 100644 test/Util/Serialization/BothAttributesUninitialized.php create mode 100644 test/Util/Serialization/OnlyNotSerializable.php create mode 100644 test/Util/Serialization/OnlyNotSerializableUninitialized.php create mode 100644 test/Util/Serialization/OnlySerializable.php create mode 100644 test/Util/Serialization/OnlySerializableUninitialized.php create mode 100644 test/Util/Serialization/SerializationExample.php create mode 100644 test/Util/Serialization/WithoutAttributes.php create mode 100644 test/Util/Serialization/WithoutAttributesUninitialized.php rename test/{ => Util}/Task/AdditionTask.php (93%) rename test/{ => Util}/Task/CallbackTask.php (95%) rename test/{ => Util}/Task/ChildExceptionTask.php (89%) rename test/{ => Util}/Task/EmptyTask.php (79%) rename test/{ => Util}/Task/ErrorTask.php (90%) rename test/{ => Util}/Task/ExitTask.php (69%) rename test/{ => Util}/Task/InterruptableSleepTask.php (81%) rename test/{ => Util}/Task/ParentExceptionTask.php (92%) rename test/{ => Util}/Task/SleepCountTask.php (97%) rename test/{ => Util}/Task/SleepStatusTask.php (93%) rename test/{ => Util}/Task/SleepTask.php (88%) rename test/{ => Util}/Task/SuppressedErrorOutputTask.php (84%) rename test/{ => Util}/Task/SynchronizedFieldTask.php (95%) rename test/{ => Util}/Task/UnsynchronizedFieldTask.php (92%) rename test/{ => Util}/Task/WarningTask.php (94%) 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 @@ +