Async/test/system/ParallelSuperVisorTest.php

510 lines
18 KiB
PHP

<?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\Constraint\FixedTimeConstraint;
use FuzeWorks\Async\Executors\ShellExecutor;
use FuzeWorks\Async\SuperVisor;
use FuzeWorks\Async\Supervisors\ParallelSuperVisor;
use FuzeWorks\Async\Task;
use FuzeWorks\Async\Tasks;
use FuzeWorks\Async\TaskStorage;
use FuzeWorks\Events;
use Mock\Handlers\ArgumentedHandler;
use PHPUnit\Framework\TestCase;
/**
* Class ParallelSuperVisorTest
*
* @todo Add test that latest output is added to Task, and not just 'any' output
*/
class ParallelSuperVisorTest extends TestCase
{
/**
* @var ShellExecutor
*/
private $executor;
/**
* @var TaskStorage
*/
private $taskStorage;
/**
* @var ParallelSuperVisor
*/
private $superVisor;
public function setUp(): void
{
// Load the TaskStorage so temporary tasks can be stored
$tasks = new Tasks();
$this->taskStorage = $tasks->getTaskStorage();
$this->taskStorage->reset();
// Clear events
Events::$listeners = [];
// And load the ShellExecutor using the execution settings
$this->executor = new ShellExecutor([
'bootstrapFile' => dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'bootstrap.php',
'workerFile' => dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'worker'
]);
$this->superVisor = new ParallelSuperVisor($this->taskStorage, $this->executor);
}
public function testClass()
{
$this->assertInstanceOf('FuzeWorks\Async\Supervisors\ParallelSuperVisor', $this->superVisor);
}
/* ---------------------------------- Writing and reading tasks ----------------------- */
/**
* @depends testClass
*/
public function testEmptyCycle()
{
$this->assertEquals(SuperVisor::FINISHED, $this->superVisor->cycle());
}
public function testToRunning()
{
// First create a dummy task
$dummyTask = new Task('testToRunning', new ArgumentedHandler(10, 'Some Output'), false);
// Write the dummy to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Assert that the status is PENDING and not running
$this->assertEquals(Task::PENDING, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is running for real
$this->assertEquals(Task::RUNNING, $dummyTask->getStatus());
$this->assertTrue($this->executor->getTaskRunning($dummyTask));
}
/**
* @depends testToRunning
*/
public function testConstrainedPending()
{
// First create a dummy task
$dummyTask = new Task('testConstrainedPending', new ArgumentedHandler(10, 'Some Output'), false);
// Add a constraint
$dummyTask->addConstraint(new FixedTimeConstraint(time() + 3600));
// Write the dummy to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Assert that the status is PENDING and not running
$this->assertEquals(Task::PENDING, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is delayed
$this->assertEquals(Task::DELAYED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
}
/**
* @depends testToRunning
*/
public function testChangeDelayedToPending()
{
// First create a dummy task
$dummyTask = new Task('testChangeDelayedToPending', new ArgumentedHandler(10, 'Some Output'), false);
// Set to delayed and set to NOW
$dummyTask->setStatus(Task::DELAYED);
$dummyTask->setDelayTime(time() - 3600);
// Write the dummy to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Assert that the status is DELAYED and not running
$this->assertEquals(Task::DELAYED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is delayed
$this->assertEquals(Task::PENDING, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
}
/**
* @depends testToRunning
*/
public function testKeepDelayed()
{
// First create a dummy task
$dummyTask = new Task('testKeepDelayed', new ArgumentedHandler(10, 'Some Output'), false);
// Set to delayed and set to NOW
$dummyTask->setStatus(Task::DELAYED);
$dummyTask->setDelayTime(time() + 3600);
// Write the dummy to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Assert that the status is DELAYED and not running
$this->assertEquals(Task::DELAYED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is delayed
$this->assertEquals(Task::DELAYED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
}
/**
* @depends testToRunning
*/
public function testFinishedTask()
{
// First create a dummy task
$dummyTask = new Task('testFinishedTask', new ArgumentedHandler(10, 'Some Output'), false);
// Set status to running
$dummyTask->setStatus(Task::RUNNING);
$dummyTask->addAttribute('pid', 1005);
// Write the dummy and some output to TaskStorage
$this->taskStorage->addTask($dummyTask);
$this->taskStorage->writeTaskOutput($dummyTask, 'Some Output', '', Task::SUCCESS);
// Test if everything is set
$this->assertEquals(Task::RUNNING, $dummyTask->getStatus());
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is finished indeed
$this->assertEquals(Task::SUCCESS, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
$this->assertEquals('Some Output', $dummyTask->getOutput());
}
/**
* @depends testFinishedTask
*/
public function testMissingTask()
{
// First create a dummy task
$dummyTask = new Task('testMissingTask', new ArgumentedHandler(10, 'Some Output'), false);
// Set status to running
$dummyTask->setStatus(Task::RUNNING);
$dummyTask->addAttribute('pid', 1006);
// Write the dummy and no output to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Test if everything is set
$this->assertEquals(Task::RUNNING, $dummyTask->getStatus());
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it has failed indeed
$this->assertEquals(Task::PFAILED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
}
/**
* @depends testFinishedTask
*/
public function testFailedTask()
{
// First create a dummy task
$dummyTask = new Task('testFailedTask', new ArgumentedHandler(10, 'Some Output'), false);
// Set status to running
$dummyTask->setStatus(Task::RUNNING);
$dummyTask->addAttribute('pid', 1007);
// Write the dummy and some output to TaskStorage
$this->taskStorage->addTask($dummyTask);
$this->taskStorage->writeTaskOutput($dummyTask, 'Some Output', 'Some Errors', Task::FAILED);
// Test if everything is set
$this->assertEquals(Task::RUNNING, $dummyTask->getStatus());
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Then re-fetch the Task
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
// And check that it is finished indeed
$this->assertEquals(Task::FAILED, $dummyTask->getStatus());
$this->assertFalse($this->executor->getTaskRunning($dummyTask));
$this->assertEquals('Some Errors', $dummyTask->getErrors());
}
/**
* @depends testFailedTask
*/
public function testRetryFailedTask()
{
// First create the dummy tasks
$dummyTaskFailedYes = new Task('testRetryFailedTaskY', new ArgumentedHandler(10, 'Some Output'), false);
$dummyTaskPFailedYes = new Task('testRetryFailedTaskPY', new ArgumentedHandler(10, 'Some Output'), false);
$dummyTaskFailedNo = new Task('testRetryFailedTaskN', new ArgumentedHandler(10, 'Some Output'), false);
$dummyTaskPFailedNo = new Task('testRetryFailedTaskPN', new ArgumentedHandler(10, 'Some Output'), false);
// Set statuses
$dummyTaskFailedYes->setStatus(Task::FAILED);
$dummyTaskPFailedYes->setStatus(Task::PFAILED);
$dummyTaskFailedNo->setStatus(Task::FAILED);
$dummyTaskPFailedNo->setStatus(Task::FAILED);
// Set retry settings
$dummyTaskFailedYes->setSettings(true, 5, 30, true, true,true);
$dummyTaskPFailedYes->setSettings(true, 5, 30, true, true,true);
$dummyTaskFailedNo->setSettings(true, 5, 30, false, false,true);
$dummyTaskPFailedNo->setSettings(true, 5, 30, false, false,true);
// Save all these tasks
$this->taskStorage->addTask($dummyTaskFailedYes);
$this->taskStorage->addTask($dummyTaskPFailedYes);
$this->taskStorage->addTask($dummyTaskFailedNo);
$this->taskStorage->addTask($dummyTaskPFailedNo);
// Then cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// Reload all tasks from TaskStorage
$dummyTaskFailedYes = $this->taskStorage->getTaskById($dummyTaskFailedYes->getId());
$dummyTaskPFailedYes = $this->taskStorage->getTaskById($dummyTaskPFailedYes->getId());
$dummyTaskFailedNo = $this->taskStorage->getTaskById($dummyTaskFailedNo->getId());
$dummyTaskPFailedNo = $this->taskStorage->getTaskById($dummyTaskPFailedNo->getId());
// Check if the tasks that should retry are running
$this->assertEquals(Task::RUNNING, $dummyTaskFailedYes->getStatus());
$this->assertEquals(Task::RUNNING, $dummyTaskPFailedYes->getStatus());
$this->assertTrue($this->executor->getTaskRunning($dummyTaskFailedYes));
$this->assertTrue($this->executor->getTaskRunning($dummyTaskPFailedYes));
// Check if the tasks that shouldn't have been cancelled
$this->assertEquals(Task::CANCELLED, $dummyTaskFailedNo->getStatus());
$this->assertEquals(Task::CANCELLED, $dummyTaskPFailedNo->getStatus());
}
/**
* @depends testFailedTask
*/
public function testExceedMaxRetries()
{
// First create the dummy tasks
$dummyTask = new Task('testExceedMaxRetries', new ArgumentedHandler(10, 'Some Output'), false);
$dummyTask2 = new Task('testExceedMaxRetries2', new ArgumentedHandler(10, 'Some Output'), false);
// Set status and retry settings
$dummyTask->setStatus(Task::FAILED);
$dummyTask2->setStatus(Task::FAILED);
$dummyTask->setSettings(true, 2, 30, true, true, true);
$dummyTask2->setSettings(true, 2, 30, true, true, true);
// Set retries to 2 for the first task, and 1 for the second task
$dummyTask->addRetry();
$dummyTask->addRetry();
$dummyTask2->addRetry();
// Write the task to TaskStorage
$this->taskStorage->addTask($dummyTask);
$this->taskStorage->addTask($dummyTask2);
// Cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// And check if the Task has been cancelled
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
$dummyTask2 = $this->taskStorage->getTaskById($dummyTask2->getId());
$this->assertEquals(Task::CANCELLED, $dummyTask->getStatus());
$this->assertEquals(Task::RUNNING, $dummyTask2->getStatus());
}
/**
* @depends testFailedTask
*/
public function testFailedToPost()
{
// First create the dummy tasks
$dummyTask = new Task('testFailedToPost', new ArgumentedHandler(10, 'Some Output'), true);
// Set status and settings
$dummyTask->setStatus(Task::FAILED);
$dummyTask->addAttribute('pid', 1010);
// Write the task to TaskStorage
$this->taskStorage->addTask($dummyTask);
// Cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// And check if the Task has been moved to Post
$dummyTask = $this->taskStorage->getTaskById($dummyTask->getId());
$this->assertEquals(Task::POST, $dummyTask->getStatus());
$this->assertTrue($this->executor->getTaskRunning($dummyTask));
$this->assertIsInt($dummyTask->attribute('pid'));
$this->assertNotEquals(1010, $dummyTask->attribute('pid'));
}
/**
* @depends testFinishedTask
*/
public function testSuccessfulTasks()
{
// First create the dummy tasks
$dummyTaskPostNo = new Task('testSuccessfulTasksN', new ArgumentedHandler(10, 'Some Output'), false);
$dummyTaskPostYes = new Task('testSuccessfulTasksY', new ArgumentedHandler(10, 'Some Output'), true);
// Set status and settings
$dummyTaskPostNo->setStatus(Task::SUCCESS);
$dummyTaskPostYes->setStatus(Task::SUCCESS);
// Write the tasks to TaskStorage
$this->taskStorage->addTask($dummyTaskPostNo);
$this->taskStorage->addTask($dummyTaskPostYes);
// Cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// And check if the Tasks have been completed or moved to post
$dummyTaskPostNo = $this->taskStorage->getTaskById($dummyTaskPostNo->getId());
$dummyTaskPostYes = $this->taskStorage->getTaskById($dummyTaskPostYes->getId());
$this->assertEquals(Task::COMPLETED, $dummyTaskPostNo->getStatus());
$this->assertEquals(Task::POST, $dummyTaskPostYes->getStatus());
$this->assertTrue($this->executor->getTaskRunning($dummyTaskPostYes));
}
public function testPostTasks()
{
// First create the dummy tasks
$dummyTaskFinished = new Task('testPostTasksFinished', new ArgumentedHandler(10, 'Some Output'), true);
$dummyTaskMissing = new Task('testPostTasksMissing', new ArgumentedHandler(10, 'Some Output'), true);
// Set status and settings
$dummyTaskFinished->setStatus(Task::POST);
$dummyTaskMissing->setStatus(Task::POST);
$dummyTaskFinished->addAttribute('pid', 1011);
$dummyTaskMissing->addAttribute('pid', 1012);
// Write the tasks to TaskStorage
$this->taskStorage->addTask($dummyTaskFinished);
$this->taskStorage->addTask($dummyTaskMissing);
$this->taskStorage->writePostOutput($dummyTaskFinished, 'Post Output', 'Post Errors', Task::SUCCESS);
// Cycle the SuperVisor
$this->superVisor->cycle();
// Pause 1/10th of a second
usleep(500000);
// And check if the Tasks have been completed or failed
$dummyTaskFinished = $this->taskStorage->getTaskById($dummyTaskFinished->getId());
$dummyTaskMissing = $this->taskStorage->getTaskById($dummyTaskMissing->getId());
$this->assertEquals(Task::COMPLETED, $dummyTaskFinished->getStatus());
$this->assertEquals(Task::CANCELLED, $dummyTaskMissing->getStatus());
$this->assertEquals('Post Output', $dummyTaskFinished->getPostOutput());
$this->assertNull($dummyTaskMissing->getPostOutput());
}
}