First commit

This commit is contained in:
Daniil Gentili 2020-07-31 15:09:34 +02:00
commit ef20c2ef10
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
23 changed files with 958 additions and 0 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: danog

49
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: build
on:
pull_request:
push:
jobs:
run:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
php-versions: ["7.1", "7.2", "7.3", "7.4"]
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, sockets
coverage: xdebug
- name: Check environment
run: |
php --version
composer --version
- name: Get composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ matrix.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ matrix.os }}-composer-${{ matrix.php-versions }}-
- name: Install dependencies
run: composer install --prefer-dist
- name: Run tests
env:
PHP_CS_FIXER_IGNORE_ENV: 1
run: |
vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml
vendor/bin/php-cs-fixer --diff --dry-run -v fix
vendor/bin/psalm

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.vscode
build
composer.lock
phpunit.xml
vendor
.php_cs.cache
coverage

13
.php_cs.dist Normal file
View File

@ -0,0 +1,13 @@
<?php
$config = new Amp\CodeStyle\Config();
$config->getFinder()
->in(__DIR__ . '/examples')
->in(__DIR__ . '/lib')
->in(__DIR__ . '/test');
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
$config->setCacheFile($cacheDir . '/.php_cs.cache');
return $config;

7
LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright 2019-2020 Daniil Gentili
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

74
README.md Normal file
View File

@ -0,0 +1,74 @@
# AsyncConstruct
![Build status](https://github.com/danog/asyncConstruct/workflows/build/badge.svg)
[![codecov](https://codecov.io/gh/danog/asyncConstruct/branch/master/graph/badge.svg)](https://codecov.io/gh/danog/asyncConstruct)
![License](https://img.shields.io/badge/license-MIT-blue.svg)
`danog/asyncConstruct` provides async class constructor and wakeup functions for AMPHP.
## Installation
```bash
composer require danog/async-construct
```
## API
* Async
* [AsyncConstruct](#AsyncConstruct)
* [AsyncWakeup](#AsyncWakeup)
* Blocking
* [BlockingConstruct](#BlockingConstruct)
* [BlockingWakeup](#BlockingWakeup)
### AsyncConstruct
[Example](https://github.com/danog/asyncConstruct/blob/master/examples/AsyncConstruct.php)
Simply `use` this trait in your class, and define a `__construct_async` async method.
The trait will automatically run the async method on construction, and expose a set of APIs to wait on the result of the constructor for initialization.
### init()
Will blockingly wait for execution of the async constructor, returning its return value.
### initAsynchronously()
Will return a promise that, when `yield`ed, will wait for and return the return value of the constructor.
### inited()
Returns a boolean, indicating whether the class was initialized or not.
### BlockingConstruct
[Example](https://github.com/danog/asyncConstruct/blob/master/examples/BlockingConstruct.php)
Exactly like [AsyncConstruct], except that the blocking `init()` function will be automatically called on construction.
### AsyncWakeup
[Example](https://github.com/danog/asyncConstruct/blob/master/examples/AsyncWakeup.php)
Simply `use` this trait in your class, and define a `__wakeup_async` async method.
The trait will automatically run the async method on deserialization, and expose a set of APIs to wait on the result of the wakeup function for re-initialization.
### wakeup()
Will blockingly wait for execution of the async wakeup function, returning its return value.
### wakeupAsynchronously()
Will return a promise that, when `yield`ed, will wait for and return the return value of the wakeup function.
### wokenUp()
Returns a boolean, indicating whether the class was initialized or not.
### BlockingWakeup
[Example](https://github.com/danog/asyncConstruct/blob/master/examples/BlockingWakeup.php)
Exactly like [AsyncWakeup], except that the blocking `wakeup()` function will be automatically called on wakeup.

48
composer.json Normal file
View File

@ -0,0 +1,48 @@
{
"name": "danog/async-construct",
"description": "Async class constructor and wakeup functions for AMPHP.",
"keywords": [
"asynchronous",
"async",
"concurrent",
"multi-threading",
"multi-processing"
],
"homepage": "https://github.com/danog/asyncConstruct",
"license": "MIT",
"authors": [
{
"name": "Daniil Gentili",
"email": "daniil@daniil.it"
}
],
"require": {
"php": ">=7.1",
"amphp/amp": "^2"
},
"require-dev": {
"phpunit/phpunit": "^7 | ^8 | ^9",
"amphp/phpunit-util": "^1.3",
"amphp/php-cs-fixer-config": "dev-master",
"vimeo/psalm": "dev-master"
},
"autoload": {
"psr-4": {
"danog\\": "lib"
}
},
"autoload-dev": {
"psr-4": {
"danog\\Test\\": "test"
}
},
"scripts": {
"check": [
"@cs",
"@test"
],
"cs": "php-cs-fixer fix -v --diff --dry-run",
"cs-fix": "php-cs-fixer fix -v --diff",
"test": "phpdbg -qrr -dzend.assertions=1 -dassert.exception=1 ./vendor/bin/phpunit --coverage-text"
}
}

34
examples/async.php Normal file
View File

@ -0,0 +1,34 @@
<?php
use Amp\Loop;
use danog\AsyncConstruct;
use function Amp\delay;
require 'vendor/autoload.php';
class AsyncTest
{
use AsyncConstruct;
/**
* Async constructor function.
*
* Typically, one would establish an async AMP database connection here, or do some other async stuff.
*
* @param integer $delay Constructor delay
*
* @return \Generator
*/
protected function __construct_async(int $delay): \Generator
{
yield delay($delay);
echo "Inited after $delay milliseconds!".PHP_EOL;
}
}
Loop::run(function () {
echo "Before starting constructor".PHP_EOL;
$test = new AsyncTest(1000);
echo "Started constructor, waiting...".PHP_EOL;
yield $test->initAsynchronously();
});

48
examples/asyncWakeup.php Normal file
View File

@ -0,0 +1,48 @@
<?php
use Amp\Loop;
use danog\AsyncWakeup;
use function Amp\delay;
require 'vendor/autoload.php';
class AsyncWakeupTest
{
use AsyncWakeup;
/**
* Time to wait for wakeup.
*
* @var int
*/
private $delay;
/**
* Constructor function.
*
* @param integer $delay
*/
public function __construct(int $delay)
{
$this->delay = $delay;
}
/**
* Async wakeup function.
*
* Typically, one would re-establish an async AMP database connection here, or do some other async stuff.
*
* @return \Generator
*/
protected function __wakeup_async(): \Generator
{
yield delay($this->delay);
echo "Woke up after {$this->delay} milliseconds!".PHP_EOL;
}
}
Loop::run(function () {
$test = new AsyncWakeupTest(1000);
$test = \serialize($test);
$test = \unserialize($test);
echo "Deserialized, waiting asynchronously...".PHP_EOL;
yield $test->wakeupAsynchronously();
});

33
examples/blocking.php Normal file
View File

@ -0,0 +1,33 @@
<?php
use Amp\Loop;
use danog\BlockingConstruct;
use function Amp\delay;
require 'vendor/autoload.php';
class BlockingTest
{
use BlockingConstruct;
/**
* Async constructor function.
*
* Typically, one would establish an async AMP database connection here, or do some other async stuff.
*
* @param integer $delay Constructor delay
*
* @return \Generator
*/
protected function __construct_async(int $delay): \Generator
{
yield delay($delay);
echo "Inited after $delay milliseconds!".PHP_EOL;
}
}
Loop::run(function () {
echo "Before starting constructor".PHP_EOL;
$test = new BlockingTest(1000);
echo "Started constructor!".PHP_EOL;
});

View File

@ -0,0 +1,47 @@
<?php
use Amp\Loop;
use danog\BlockingWakeup;
use function Amp\delay;
require 'vendor/autoload.php';
class BlockingWakeupTest
{
use BlockingWakeup;
/**
* Time to wait for wakeup.
*
* @var int
*/
private $delay;
/**
* Constructor function.
*
* @param integer $delay
*/
public function __construct(int $delay)
{
$this->delay = $delay;
}
/**
* Async wakeup function.
*
* Typically, one would re-establish an async AMP database connection here, or do some other async stuff.
*
* @return \Generator
*/
protected function __wakeup_async(): \Generator
{
yield delay($this->delay);
echo "Woke up after {$this->delay} milliseconds!".PHP_EOL;
}
}
Loop::run(function () {
$test = new BlockingWakeupTest(1000);
$test = \serialize($test);
echo "Deserializing...".PHP_EOL;
$test = \unserialize($test);
});

105
lib/AsyncConstruct.php Normal file
View File

@ -0,0 +1,105 @@
<?php
/**
* Async constructor trait.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/MIT MIT
*/
namespace danog;
use Amp\Coroutine;
use Amp\Promise;
use Amp\Success;
use function Amp\Promise\wait;
/**
* Async constructor trait.
*
* Manages asynchronous construction of async classes
*
* @author Daniil Gentili <daniil@daniil.it>
*/
trait AsyncConstruct
{
/**
* Async init promise.
*
* @var ?Promise
*/
private $asyncInitPromise;
/**
* Constructor function.
*
* @param mixed ...$params Parameters
*/
public function __construct(...$params)
{
if (\method_exists($this, '__construct_async')) {
$this->setInitPromise($this->__construct_async(...$params));
}
}
/**
* Set init promise.
*
* @param Promise|\Generator $promise Promise
*
* @return void
*/
protected function setInitPromise($promise): void
{
$this->asyncInitPromise = $promise instanceof \Generator ? new Coroutine($promise) : $promise;
$this->asyncInitPromise->onResolve(
function (?\Throwable $error, $result): void {
$this->asyncInitPromise = null;
}
);
}
/**
* Blockingly init.
*
* @return mixed Resolves with the result of the constructor or null
*/
public function init()
{
if ($this->asyncInitPromise) {
return wait($this->asyncInitPromise);
}
return null;
}
/**
* Asynchronously init.
*
* @return Promise<mixed|null> Resolves with the result of the constructor or null
*/
public function initAsynchronously(): Promise
{
if ($this->asyncInitPromise) {
return $this->asyncInitPromise;
}
return new Success();
}
/**
* Check if we've already inited.
*
* @return boolean
*/
public function inited(): bool
{
return !$this->asyncInitPromise;
}
/**
* Mark instance as (de)inited forcefully.
*
* @param boolean $inited Whether to mark the instance as inited or deinited
*
* @return void
*/
public function forceInit(bool $inited): void
{
$this->asyncInitPromise = $inited ? null : new Success(null);
}
}

103
lib/AsyncWakeup.php Normal file
View File

@ -0,0 +1,103 @@
<?php
/**
* Async wakeup trait.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/MIT MIT
*/
namespace danog;
use Amp\Coroutine;
use Amp\Promise;
use Amp\Success;
use function Amp\Promise\wait;
/**
* Async wakeup trait.
*
* Manages asynchronous wakeup of async classes
*
* @author Daniil Gentili <daniil@daniil.it>
*/
trait AsyncWakeup
{
/**
* Async wakeup promise.
*
* @var ?Promise
*/
private $asyncWakeupPromise;
/**
* Wakeup function.
*/
public function __wakeup(): void
{
if (\method_exists($this, '__wakeup_async')) {
$this->setWakeupPromise($this->__wakeup_async());
}
}
/**
* Set wakeup promise.
*
* @param Promise|\Generator $promise Promise
*
* @return void
*/
protected function setWakeupPromise($promise): void
{
$this->asyncWakeupPromise = $promise instanceof \Generator ? new Coroutine($promise) : $promise;
$this->asyncWakeupPromise->onResolve(
function (?\Throwable $error, $result): void {
$this->asyncWakeupPromise = null;
}
);
}
/**
* Blockingly wakeup.
*
* @return mixed|null Resolves with the result of the wakeup function or null
*/
public function wakeup()
{
if ($this->asyncWakeupPromise) {
return wait($this->asyncWakeupPromise);
}
return null;
}
/**
* Asynchronously wakeup.
*
* @return Promise<mixed|null> Resolves with the result of the wakeup function or null
*/
public function wakeupAsynchronously(): Promise
{
if ($this->asyncWakeupPromise) {
return $this->asyncWakeupPromise;
}
return new Success();
}
/**
* Check if we've already woken up.
*
* @return boolean
*/
public function wokenUp(): bool
{
return !$this->asyncWakeupPromise;
}
/**
* Mark instance as (de)woken up forcefully.
*
* @param boolean $woke Whether to mark the instance as woken up or not
*
* @return void
*/
public function forceWakeup(bool $woke): void
{
$this->asyncWakeupPromise = $woke ? null : new Success(null);
}
}

36
lib/BlockingConstruct.php Normal file
View File

@ -0,0 +1,36 @@
<?php
/**
* Blocking constructor trait.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/MIT MIT
*/
namespace danog;
/**
* Blocking constructor trait.
*
* Manages blocking construction of async classes
*
* @author Daniil Gentili <daniil@daniil.it>
*/
trait BlockingConstruct
{
use AsyncConstruct;
/**
* Constructor function.
*
* @param mixed ...$params Parameters
*/
public function __construct(...$params)
{
if (\method_exists($this, '__construct_async')) {
$this->setInitPromise($this->__construct_async(...$params));
}
$this->init();
}
}

34
lib/BlockingWakeup.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* Blocking wakeup trait.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2020 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/MIT MIT
*/
namespace danog;
/**
* Blocking wakeup trait.
*
* Manages blocking wakeup of async classes
*
* @author Daniil Gentili <daniil@daniil.it>
*/
trait BlockingWakeup
{
use AsyncWakeup;
/**
* Wakeup function.
*/
public function __wakeup(): void
{
if (\method_exists($this, '__wakeup_async')) {
$this->setWakeupPromise($this->__wakeup_async());
}
$this->wakeup();
}
}

28
phpunit.xml.dist Normal file
View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Amp Concurrent">
<directory>test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">lib</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="build/coverage"/>
</logging>
</phpunit>

15
psalm.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="lib" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

89
test/ConstructTest.php Normal file
View File

@ -0,0 +1,89 @@
<?php
namespace danog\Test;
use Amp\PHPUnit\AsyncTestCase;
use danog\AsyncConstruct;
use danog\BlockingConstruct;
use danog\Test\Fixtures\AsyncConstructTest;
use danog\Test\Fixtures\BlockingConstructTest;
use function Amp\delay;
class ConstructTest extends AsyncTestCase
{
public function testAsyncInitBackground(): \Generator
{
$test = new AsyncConstructTest(100);
$this->assertFalse($test->inited());
$this->assertFalse($test->inited);
yield delay(50);
$this->assertFalse($test->inited());
$this->assertFalse($test->inited);
yield delay(51);
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
}
public function testAsyncInit(): \Generator
{
$test = new AsyncConstructTest(100);
$this->assertTrue(yield $test->initAsynchronously());
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
$this->assertNull(yield $test->initAsynchronously());
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
}
public function testAsyncInitBlocking()
{
$test = new AsyncConstructTest(100);
$this->assertTrue($test->init());
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
$this->assertNull($test->init());
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
}
public function testBlockingInit()
{
$test = new BlockingConstructTest(100);
$this->assertTrue($test->inited());
$this->assertTrue($test->inited);
$this->assertNull($test->init());
}
public function testNoInit()
{
$test = new class(100) {
use AsyncConstruct;
};
$this->assertTrue($test->inited());
$test = new class(100) {
use BlockingConstruct;
};
$this->assertTrue($test->inited());
}
public function testForceInit()
{
$test = new AsyncConstructTest(100);
$test->forceInit(true);
$this->assertTrue($test->inited());
$this->assertNull($test->init());
$test->forceInit(false);
$this->assertFalse($test->inited());
$this->assertNull($test->init());
$test = new BlockingConstructTest(100);
$test->forceInit(true);
$this->assertTrue($test->inited());
$this->assertNull($test->init());
$test->forceInit(false);
$this->assertFalse($test->inited());
$this->assertNull($test->init());
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace danog\Test\Fixtures;
use danog\AsyncConstruct;
use function Amp\delay;
class AsyncConstructTest
{
use AsyncConstruct;
public $inited = false;
protected function __construct_async(int $delay): \Generator
{
yield delay($delay);
$this->inited = true;
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace danog\Test\Fixtures;
use danog\AsyncWakeup as AWakeup;
use function Amp\delay;
class AsyncWakeupTest
{
use AWakeup;
public $woke = false;
private $delay;
public function __construct(int $delay)
{
$this->delay = $delay;
}
protected function __wakeup_async(): \Generator
{
yield delay($this->delay);
$this->woke = true;
return true;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace danog\Test\Fixtures;
use danog\BlockingConstruct;
use function Amp\delay;
class BlockingConstructTest
{
use BlockingConstruct;
public $inited = false;
protected function __construct_async(int $delay): \Generator
{
yield delay($delay);
$this->inited = true;
return true;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace danog\Test\Fixtures;
use danog\BlockingWakeup;
use function Amp\delay;
class BlockingWakeupTest
{
use BlockingWakeup;
public $woke = false;
private $delay;
public function __construct(int $delay)
{
$this->delay = $delay;
}
protected function __wakeup_async(): \Generator
{
yield delay($this->delay);
$this->woke = true;
return true;
}
}

101
test/WakeupTest.php Normal file
View File

@ -0,0 +1,101 @@
<?php
namespace danog\Test;
use Amp\PHPUnit\AsyncTestCase;
use danog\AsyncWakeup;
use danog\BlockingWakeup;
use danog\Test\Fixtures\AsyncWakeupTest as AsyncWakeupTest;
use danog\Test\Fixtures\BlockingWakeupTest;
use function Amp\delay;
class WakeupTest extends AsyncTestCase
{
public function testAsyncWakeupBackground(): \Generator
{
$test = new AsyncWakeupTest(100);
$this->assertFalse($test->woke);
$test = \unserialize(\serialize($test));
$this->assertFalse($test->wokenUp());
$this->assertFalse($test->woke);
yield delay(50);
$this->assertFalse($test->wokenUp());
$this->assertFalse($test->woke);
yield delay(51);
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
}
public function testAsyncWakeup(): \Generator
{
$test = new AsyncWakeupTest(100);
$this->assertFalse($test->woke);
$test = \unserialize(\serialize($test));
$this->assertTrue(yield $test->WakeupAsynchronously());
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
$this->assertNull(yield $test->WakeupAsynchronously());
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
}
public function testAsyncWakeupBlocking()
{
$test = new AsyncWakeupTest(100);
$this->assertFalse($test->woke);
$test = \unserialize(\serialize($test));
$this->assertTrue($test->Wakeup());
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
$this->assertNull($test->Wakeup());
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
}
public function testBlockingWakeup()
{
$test = new BlockingWakeupTest(100);
$this->assertFalse($test->woke);
$test = \unserialize(\serialize($test));
$this->assertTrue($test->wokenUp());
$this->assertTrue($test->woke);
$this->assertNull($test->Wakeup());
}
public function testNoWakeup()
{
$test = new class(100) {
use AsyncWakeup;
};
$this->assertTrue($test->wokenUp());
$test = new class(100) {
use BlockingWakeup;
};
$this->assertTrue($test->wokenUp());
}
public function testForceWakeup()
{
$test = new AsyncWakeupTest(100);
$test->forceWakeup(true);
$this->assertTrue($test->wokenUp());
$this->assertNull($test->Wakeup());
$test->forceWakeup(false);
$this->assertFalse($test->wokenUp());
$this->assertNull($test->Wakeup());
$test = new BlockingWakeupTest(100);
$test->forceWakeup(true);
$this->assertTrue($test->wokenUp());
$this->assertNull($test->Wakeup());
$test->forceWakeup(false);
$this->assertFalse($test->wokenUp());
$this->assertNull($test->Wakeup());
}
}