Finished ControllerHandler into a working state.
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Abel Hoogeveen 2020-06-07 15:47:51 +02:00
parent 741845d686
commit 164e7d877c
No known key found for this signature in database
GPG Key ID: 96C2234920BF4292
6 changed files with 466 additions and 37 deletions

View File

@ -58,14 +58,60 @@ class ControllerHandler implements Handler
*/
protected $controllerMethod;
/**
* The namespace to use to load the controller
*
* @var string
*/
protected $controllerNamespace;
/**
* @var string|null The method used to handle the post phase; if requested
*/
protected $postMethod = null;
/**
* @var string The output to be returned to ShellWorker
*/
protected $output;
/**
* @var string The postOutput to be returned to ShellWorker
*/
protected $postOutput;
/**
* Input imported from the parent handler
*
* @var string|null
*/
protected $parentInput;
/**
* ControllerHandler constructor.
*
* Provides all information of which controller to use. Requests get redirected to that controller.
*
* @param string $controllerName The name of the controller to use
* @param string $controllerMethod The method to use for the task execution
* @param string|null $postMethod The method to use for the post execution
* @param string $controllerNamespace A potential custom namespace for the controller
*/
public function __construct(string $controllerName, string $controllerMethod, string $postMethod = null, string $controllerNamespace = '\Application\Controller\\')
{
$this->controllerName = $controllerName;
$this->controllerMethod = $controllerMethod;
$this->postMethod = $postMethod;
$this->controllerNamespace = $controllerNamespace;
}
/**
* @inheritDoc
*/
public function init(Task $task)
{
}
/**
* @inheritDoc
* @throws TasksException
@ -73,24 +119,35 @@ class ControllerHandler implements Handler
public function primaryHandler(Task $task): bool
{
// Set the arguments
$args = $this->setArguments($task);
$args = $task->getArguments();
array_unshift($args, $task);
// First we fetch the controller
$controller = $this->getController($this->controllerName);
$controller = $this->getController();
// Check if method exists
if (!method_exists($controller, $this->controllerMethod))
throw new TasksException("Could not handle task. Method '$this->controllerMethod' not found on controller.");
if (!method_exists($controller, 'getTaskStatus'))
throw new TasksException("Could not handle task. Method 'getTaskStatus()' not found on controller, which is required.");
if ($this->parentInput !== null && method_exists($controller, 'setInput'))
$controller->setInput($this->parentInput);
// Call method and collect output
$this->output = call_user_func_array([$controller, $this->controllerMethod], $args);
return true;
$success = $controller->getTaskStatus();
if (!is_bool($success))
throw new TasksException("Could not determine whether task has succeeded. getTaskStatus() returned non-bool.");
return $success;
}
/**
* @inheritDoc
*/
public function getOutput()
public function getOutput(): string
{
return $this->output;
}
@ -101,61 +158,45 @@ class ControllerHandler implements Handler
*/
public function postHandler(Task $task)
{
// Set the arguments
$this->setArguments($task);
// Abort if no postMethod exists
if (is_null($this->postMethod))
throw new TasksException("Could not handle task. No post method provided.");
// First we fetch the controller
$controller = $this->getController($this->controllerName);
$controller = $this->getController();
// Check if method exists
if (!method_exists($controller, $this->postMethod))
throw new TasksException("Could not handle task. Post method '$this->postMethod' not found on controller.");
if (!method_exists($controller, 'getTaskStatus'))
throw new TasksException("Could not handle task. Method 'getTaskStatus()' not found on controller, which is required.");
if ($this->parentInput !== null && method_exists($controller, 'setInput'))
$controller->setInput($this->parentInput);
// Call method and collect output
$this->postOutput = call_user_func_array([$controller, $this->postMethod], [$task]);
return true;
$success = $controller->getTaskStatus();
if (!is_bool($success))
throw new TasksException("Could not determine whether task has succeeded. getTaskStatus() returned non-bool.");
return $success;
}
/**
* @inheritDoc
*/
public function getPostOutput()
public function getPostOutput(): string
{
return $this->postOutput;
}
/**
* Set the arguments of this handler using the provided task
*
* @param Task $task
* @return array
* @throws TasksException
*/
public function setArguments(Task $task): array
{
// Direct arguments
$args = $task->getArguments();
if (count($args) < 2)
throw new TasksException("Could not handle task. Not enough arguments provided.");
// First argument: controllerName
$this->controllerName = $args[0];
$this->controllerMethod = $args[1];
$this->postMethod = isset($args[2]) ? $args[2] : null;
return !array_key_exists(2, $args) ? [] : array_slice($args, 3);
}
/**
* @param string $controllerName
* @return Controller
* @throws TasksException
*/
private function getController(string $controllerName): Controller
private function getController(): Controller
{
// First load the controllers component
try {
@ -163,7 +204,7 @@ class ControllerHandler implements Handler
$controllers = Factory::getInstance('controllers');
// Load the requested controller
return $controllers->get($controllerName);
return $controllers->get($this->controllerName, [], $this->controllerNamespace);
} catch (FactoryException $e) {
throw new TasksException("Could not get controller. FuzeWorks\MVCR is not installed!");
} catch (ControllerException $e) {
@ -172,4 +213,33 @@ class ControllerHandler implements Handler
throw new TasksException("Could not get controller. Controller was not found.");
}
}
/**
* @var Handler
*/
private $parentHandler;
/**
* @inheritDoc
*/
public function getParentHandler(): ?Handler
{
return $this->parentHandler;
}
/**
* @inheritDoc
*/
public function setParentHandler(Handler $parentHandler): void
{
$this->parentHandler = $parentHandler;
}
/**
* @inheritDoc
*/
public function setParentInput(string $input): void
{
$this->parentInput = $input;
}
}

View File

@ -225,7 +225,10 @@ class ParallelSuperVisor implements SuperVisor
elseif (!$isRunning && $hasOutput)
{
$task->setPostOutput($output['output'], $output['errors']);
$task->setStatus(Task::COMPLETED);
if ($output['statusCode'] === Task::SUCCESS)
$task->setStatus(Task::COMPLETED);
else
$task->setStatus(Task::CANCELLED);
}
// If the task is still running, leave it be
else

View File

@ -0,0 +1,195 @@
<?php
/**
* FuzeWorks Async Library
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2020 TechFuze
*
* 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.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2020, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
use FuzeWorks\Async\Handler\ControllerHandler;
use FuzeWorks\Async\SuperVisor;
use FuzeWorks\Async\Task;
use FuzeWorks\Async\Tasks;
use FuzeWorks\Async\TaskStorage;
use FuzeWorks\Events;
use FuzeWorks\Factory;
use Mock\Handlers\EmptyHandler;
use PHPUnit\Framework\TestCase;
class ControllerHandlerTest extends TestCase
{
/**
* @var Tasks
*/
protected $tasks;
/**
* @var TaskStorage
*/
protected $taskStorage;
public function setUp(): void
{
// Add TaskStorage
/** @var Tasks $tasks */
$this->tasks = Factory::getInstance('libraries')->get('async');
$this->taskStorage = $this->tasks->getTaskStorage();
$this->taskStorage->reset();
// Reset events
Events::$listeners = [];
}
/* ---------------------------------- Test the class itself --------------------------- */
public function testParametersAndClass()
{
// Create a test class
$handler = new ControllerHandler('TestController', 'testMethod', 'testPostMethod', '\Test\Namespace\\');
$parentHandler = new EmptyHandler();
// Set some parameters
$handler->setParentHandler($parentHandler);
$handler->setParentInput('Some Parent Input');
// And test return values
$this->assertInstanceOf(ControllerHandler::class, $handler);
$this->assertSame($parentHandler, $handler->getParentHandler());
}
public function testEmptyController()
{
// Create the handler
$handler = new ControllerHandler('empty', 'primary', 'post', '\Mock\Controllers\\');
$handler->setParentInput('Some input');
// And create the dummy Task
$dummyTask = new Task('testEmptyController', $handler, true, 'para1', 'para2');
// Write the task to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Get the SuperVisor and start running the build
$superVisor = $this->tasks->getSuperVisor();
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::RUNNING,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Give the task some time to finish
usleep(750000);
// Assert that the task is now waiting in POST
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::SUCCESS,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Cycle again so it goes into POST mode
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::POST,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Give the task some extra time to finish
usleep(750000);
// Cycle again so it goes into POST mode
$this->assertEquals(SuperVisor::FINISHED, $superVisor->cycle());
$this->assertEquals(Task::COMPLETED,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Now that the task is finished, let's see if the results match expectations
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
$this->assertEquals('Primary success: para1 + para2 + Some input', $dummyTask->getOutput());
$this->assertEquals('Post success: testEmptyController', $dummyTask->getPostOutput());
}
/**
* @depends testEmptyController
*/
public function testFailingController()
{
// Create the handler
$handler = new ControllerHandler('failing', 'primary', 'post', '\Mock\Controllers\\');
// And create the dummy Task
$dummyTask = new Task('testFailingController', $handler, true, 'para1', 'para2');
// Set this task to not retry
$dummyTask->setSettings(false);
// Write the task to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Get the SuperVisor and start running the build
$superVisor = $this->tasks->getSuperVisor();
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::RUNNING,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Give the task some time to finish
usleep(750000);
// Assert that the task is now waiting in POST
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::FAILED,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// The task should not retry and move to POST now
$this->assertEquals(SuperVisor::RUNNING, $superVisor->cycle());
$this->assertEquals(Task::POST,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Give the task some extra time to finish
usleep(750000);
// The task should not retry and move to POST now
$this->assertEquals(SuperVisor::FINISHED, $superVisor->cycle());
$this->assertEquals(Task::CANCELLED,
$this->taskStorage->getTaskById($dummyTask->getId())->getStatus()
);
// Now that the task is finished, let's see if the results match expectations
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
$this->assertEquals('Primary success: para1 + para2', $dummyTask->getOutput());
$this->assertEquals('Post success: testFailingController', $dummyTask->getPostOutput());
$this->assertEquals('ERROR \'Logged some task error!\'', $dummyTask->getErrors());
$this->assertEquals('ERROR \'Logged some post error!\'', $dummyTask->getPostErrors());
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* FuzeWorks Async Library
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2020 TechFuze
*
* 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.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2020, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace Mock\Controllers;
use FuzeWorks\Async\Task;
use FuzeWorks\Controller;
class EmptyController extends Controller
{
public $input;
public $task;
public $param1;
public $param2;
public $postTask;
public function setInput(string $input)
{
$this->input = $input;
}
public function primary(Task $task, $param1, $param2)
{
$this->task = $task;
$this->param1 = $param1;
$this->param2 = $param2;
return "Primary success: " . $param1 . ' + ' . $param2 . ' + ' . $this->input;
}
public function post(Task $task)
{
$this->postTask = $task;
return "Post success: " . $task->getId();
}
public function getTaskStatus(): bool
{
return true;
}
}

View File

@ -0,0 +1,83 @@
<?php
/**
* FuzeWorks Async Library
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2020 TechFuze
*
* 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.
*
* @author TechFuze
* @copyright Copyright (c) 2013 - 2020, TechFuze. (http://techfuze.net)
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 1.0.0
*
* @version Version 1.0.0
*/
namespace Mock\Controllers;
use FuzeWorks\Async\Task;
use FuzeWorks\Controller;
use FuzeWorks\Logger;
class FailingController extends Controller
{
public $input;
public $task;
public $param1;
public $param2;
public $postTask;
public function setInput(string $input)
{
$this->input = $input;
}
public function primary(Task $task, $param1, $param2)
{
$this->task = $task;
$this->param1 = $param1;
$this->param2 = $param2;
// Also log some errors
Logger::logError('Logged some task error!');
return "Primary success: " . $param1 . ' + ' . $param2;
}
public function post(Task $task)
{
$this->postTask = $task;
// Also log some errors
Logger::logError('Logged some post error!');
return "Post success: " . $task->getId();
}
public function getTaskStatus(): bool
{
return false;
}
}

View File

@ -490,7 +490,7 @@ class ParallelSuperVisorTest extends TestCase
// Write the tasks to TaskStorage
$this->taskStorage->addTask($dummyTaskFinished);
$this->taskStorage->addTask($dummyTaskMissing);
$this->taskStorage->writePostOutput($dummyTaskFinished, 'Post Output', 'Post Errors', Task::COMPLETED);
$this->taskStorage->writePostOutput($dummyTaskFinished, 'Post Output', 'Post Errors', Task::SUCCESS);
// Cycle the SuperVisor
$this->superVisor->cycle();