diff --git a/bin/console b/bin/console
index 5982037aa6..267910c063 100755
--- a/bin/console
+++ b/bin/console
@@ -30,6 +30,13 @@ use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
use Alchemy\Phrasea\Command\CheckConfig;
use Alchemy\Phrasea\Command\Setup\XSendFileMappingGenerator;
use Alchemy\Phrasea\Command\Setup\XSendFileConfigurationDumper;
+use Alchemy\Phrasea\Command\Task\SchedulerStart;
+use Alchemy\Phrasea\Command\Task\SchedulerState;
+use Alchemy\Phrasea\Command\Task\SchedulerStop;
+use Alchemy\Phrasea\Command\Task\TaskList;
+use Alchemy\Phrasea\Command\Task\TaskRun;
+use Alchemy\Phrasea\Command\Task\TaskState;
+use Alchemy\Phrasea\Command\Task\TaskManagerCommand;
require_once __DIR__ . '/../lib/autoload.php';
@@ -73,12 +80,14 @@ $cli->command(new \module_console_systemBackupDB('system:backup-db'));
$cli->command(new \module_console_systemClearCache('system:clear-cache'));
$cli->command(new \module_console_systemExport('system:export'));
-$cli->command(new \module_console_taskrun('task:run'));
-$cli->command(new \module_console_tasklist('task:list'));
-$cli->command(new \module_console_taskState('task:state'));
-$cli->command(new \module_console_schedulerState('scheduler:state'));
-$cli->command(new \module_console_schedulerStop('scheduler:stop'));
-$cli->command(new \module_console_schedulerStart('scheduler:start'));
+$cli->command(new TaskRun());
+$cli->command(new TaskList());
+$cli->command(new TaskState());
+$cli->command(new SchedulerStart());
+$cli->command(new SchedulerStop());
+$cli->command(new SchedulerState());
+$cli->command(new TaskManagerCommand());
+$cli->command(new TaskList());
$cli->command(new MailTest('mail:test'));
diff --git a/lib/Alchemy/Phrasea/Command/Task/SchedulerStart.php b/lib/Alchemy/Phrasea/Command/Task/SchedulerStart.php
new file mode 100644
index 0000000000..179f5949b3
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/SchedulerStart.php
@@ -0,0 +1,31 @@
+setDescription('Starts Phraseanet scheduler');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $this->container['task-manager.status']->start();
+ $output->writeln("Task manager has been toggled on start, please be sure the process is running");
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/SchedulerState.php b/lib/Alchemy/Phrasea/Command/Task/SchedulerState.php
new file mode 100644
index 0000000000..41a3fc5033
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/SchedulerState.php
@@ -0,0 +1,53 @@
+setDescription('Returns Phraseanet scheduler status')
+ ->addOption('short', null, InputOption::VALUE_NONE, 'print short result, ie: stopped() | started(12345) | tostop(12345) | stopping(12345)');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $info = $this->container['task-manager.live-information']->getManager();
+ $error = $info['configuration'] !== $info['actual'];
+ $actual = $error ? "" .$info['actual']. "" : "".$info['actual']."";
+ $configuration = $error ? "".$info['configuration']."" : "".$info['configuration']."";
+
+ if (null === $info['process-id']) {
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s', $actual));
+ } else {
+ $output->writeln(sprintf('Scheduler is %s (configured with `%s`)', $actual, $configuration));
+ }
+ } else {
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s(%s)', $actual, $info['process-id']));
+ } else {
+ $output->writeln(sprintf('Scheduler is %s (configured with `%s`) with process-id %d', $actual, $configuration, $info['process-id']));
+ }
+ }
+
+ return (int) $error;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/SchedulerStop.php b/lib/Alchemy/Phrasea/Command/Task/SchedulerStop.php
new file mode 100644
index 0000000000..1505772792
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/SchedulerStop.php
@@ -0,0 +1,31 @@
+setDescription('Starts Phraseanet scheduler');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $this->container['task-manager.status']->stop();
+ $output->writeln("Task manager has been toggled on stop, please be sure the process is running");
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/TaskList.php b/lib/Alchemy/Phrasea/Command/Task/TaskList.php
new file mode 100644
index 0000000000..0f3c97aeff
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/TaskList.php
@@ -0,0 +1,55 @@
+container['task-manager.live-information'];
+
+ $rows = array_map(function (Task $task) use ($probe, &$errors) {
+ $info = $probe->getTask($task);
+ $error = $info['actual'] !== $info['configuration'];
+ if ($error) {
+ $errors ++;
+ }
+
+ return array(
+ $task->getId(),
+ $task->getName(),
+ $task->getStatus(),
+ $error ? "" . $info['actual'] . "" : $info['actual'],
+ $info['process-id'],
+ );
+ }, $this->container['manipulator.task']->getRepository()->findAll());
+
+ $this
+ ->getHelperSet()->get('table')
+ ->setHeaders(array('Id', 'Name', 'Status', 'Actual', 'Process Id'))
+ ->setRows($rows)
+ ->render($output);
+
+ return $errors;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/TaskManagerCommand.php b/lib/Alchemy/Phrasea/Command/Task/TaskManagerCommand.php
new file mode 100644
index 0000000000..8f2dd6b96e
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/TaskManagerCommand.php
@@ -0,0 +1,45 @@
+container['task-manager']->stop();
+ break;
+ }
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ declare(ticks=1);
+ $this->container['signal-handler']->register(array(SIGINT, SIGTERM), array($this, 'signalHandler'));
+ $this->container['task-manager']->addSubscriber(new LockFileSubscriber($this->container['task-manager.logger'], $this->container['root.path'].'/tmp/locks'));
+ $this->container['task-manager']->start();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/TaskRun.php b/lib/Alchemy/Phrasea/Command/Task/TaskRun.php
new file mode 100644
index 0000000000..48adf78c59
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/TaskRun.php
@@ -0,0 +1,71 @@
+addArgument('task_id', InputArgument::REQUIRED, 'The id of the task to run', null)
+ ->addOption('max-memory', null, InputOption::VALUE_REQUIRED, '')
+ ->addOption('max-duration', null, InputOption::VALUE_REQUIRED, '')
+ ->addOption('listen-signal', null, InputOption::VALUE_NONE, '')
+ ;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ declare(ticks=1);
+
+ if (null === $task = $this->container['manipulator.task']->getRepository()->find($input->getArgument('task_id'))) {
+ var_dump($task);
+ throw new RuntimeException('Invalid task_id');
+ }
+
+ $job = $factory = $this->container['task-manager.job-factory']->create($task->getJobId());
+ $logger = $this->container['task-manager.logger'];
+
+ $job->addSubscriber(new LockFileSubscriber('task-'.$task->getId(), $logger, $this->container['root.path'].'/tmp/locks'));
+ $job->addSubscriber(new StopSignalSubscriber($this->container['signal-handler'], $logger));
+
+ if ($input->getOption('listen-signal')) {
+ $job->addSubscriber(new SignalControlledSubscriber($this->container['signal-handler'], 2, $logger));
+ }
+ if (null !== $maxDuration = $input->getOption('max-duration')) {
+ $job->addSubscriber(new DurationLimitSubscriber($maxDuration, $logger));
+ }
+ if (null !== $maxMemory = $input->getOption('max-memory')) {
+ $job->addSubscriber(new MemoryLimitSubscriber($maxMemory, $logger));
+ }
+ if ($task->isSingleRun()) {
+ $job->addSubscriber(new FinishedJobRemoverSubscriber($this->container['EM']));
+ }
+
+ $job->run(new JobData($this->container, $task));
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Command/Task/TaskState.php b/lib/Alchemy/Phrasea/Command/Task/TaskState.php
new file mode 100644
index 0000000000..8aa81b7937
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Task/TaskState.php
@@ -0,0 +1,60 @@
+addArgument('task_id', InputArgument::REQUIRED, 'The task_id to test')
+ ->setDescription('Returns a Phraseanet task state given its id')
+ ->addOption('short', null, InputOption::VALUE_NONE, 'print short result, ie: stopped() | started(12345) | tostop(12345) | ...');
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ $task_id = $input->getArgument('task_id');
+ if (null === $task = $this->container['manipulator.task']->getRepository()->find($task_id)) {
+ throw new RuntimeException('Invalid task_id');
+ }
+
+ $info = $this->container['task-manager.live-information']->getTask($task);
+ $error = $info['configuration'] !== $info['actual'];
+ $actual = $error ? "" .$info['actual']. "" : "".$info['actual']."";
+ $configuration = $error ? "".$info['configuration']."" : "".$info['configuration']."";
+
+ if (null === $info['process-id']) {
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s', $actual));
+ } else {
+ $output->writeln(sprintf('Task is %s (configured with `%s`)', $actual, $configuration));
+ }
+ } else {
+ if ($input->getOption('short')) {
+ $output->writeln(sprintf('%s(%s)', $actual, $info['process-id']));
+ } else {
+ $output->writeln(sprintf('Task is %s (configured with `%s`) with process-id %d', $actual, $configuration, $info['process-id']));
+ }
+ }
+
+ return (int) $error;
+ }
+}
diff --git a/lib/classes/module/console/schedulerStart.php b/lib/classes/module/console/schedulerStart.php
deleted file mode 100644
index 3dc9255bfd..0000000000
--- a/lib/classes/module/console/schedulerStart.php
+++ /dev/null
@@ -1,65 +0,0 @@
-setDescription('Starts Phraseanet scheduler');
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- $logger = new Logger('Task logger');
-
- $streamHandler = new Handler\StreamHandler('php://stdout', $input->getOption('verbose') ? Logger::DEBUG : Logger::WARNING);
- $logger->pushHandler($streamHandler);
-
- $logfile = __DIR__ . '/../../../../logs/scheduler.log';
- $rotateHandler = new Handler\RotatingFileHandler($logfile, 10);
- $logger->pushHandler($rotateHandler);
-
- try {
- $scheduler = new task_Scheduler($this->container, $logger);
- $scheduler->run();
- } catch (\Exception $e) {
- switch ($e->getCode()) {
- // 114 : aka EALREADY (Operation already in progress)
- case task_Scheduler::ERR_ALREADY_RUNNING:
- $exitCode = task_Scheduler::ERR_ALREADY_RUNNING;
- break;
- default:
- $exitCode = 1; // default exit code (error)
- break;
- }
-
- return $exitCode;
- }
- }
-}
diff --git a/lib/classes/module/console/schedulerState.php b/lib/classes/module/console/schedulerState.php
deleted file mode 100644
index c5c1c144e0..0000000000
--- a/lib/classes/module/console/schedulerState.php
+++ /dev/null
@@ -1,85 +0,0 @@
- 13,
- \task_manager::STATE_STARTED => 10,
- \task_manager::STATE_STOPPING => 12,
- \task_manager::STATE_STOPPED => 11,
- );
-
- public function __construct($name = null)
- {
- parent::__construct($name);
-
- $this->setDescription('Returns Phraseanet scheduler status');
-
- $this->addOption(
- 'short'
- , NULL
- , InputOption::VALUE_NONE
- , 'print short result, ie: stopped() | started(12345) | tostop(12345) | stopping(12345)'
- , NULL
- );
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- if (!$this->container['phraseanet.configuration-tester']->isInstalled()) {
- return self::EXITCODE_SETUP_ERROR;
- }
-
- $task_manager = $this->container['task-manager'];
-
- $exitCode = 0;
- $state = $task_manager->getSchedulerState();
-
- if ($input->getOption('short')) {
- $output->writeln(sprintf('%s(%s)', $state['status'], $state['pid']));
- } else {
- if ($state['pid'] != NULL) {
- $output->writeln(sprintf(
- 'Scheduler is %s on pid %d'
- , $state['status']
- , $state['pid']
- ));
- } else {
- $output->writeln(sprintf('Scheduler is %s', $state['status']));
- }
- }
-
- if (array_key_exists($state['status'], $this->stateToExitCode)) {
- $exitCode = $this->stateToExitCode[$state['status']];
- } else {
- $exitCode = self::EXITCODE_STATE_UNKNOWN;
- }
-
- return $exitCode;
- }
-}
diff --git a/lib/classes/module/console/schedulerStop.php b/lib/classes/module/console/schedulerStop.php
deleted file mode 100644
index bf142d068a..0000000000
--- a/lib/classes/module/console/schedulerStop.php
+++ /dev/null
@@ -1,47 +0,0 @@
-setDescription('Stops Phraseanet scheduler');
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- try {
- $task_manager = $this->container['task-manager'];
- $task_manager->setSchedulerState(task_manager::STATE_TOSTOP);
-
- return 0;
- } catch (\Exception $e) {
- return 1;
- }
-
- return 0;
- }
-}
diff --git a/lib/classes/module/console/taskState.php b/lib/classes/module/console/taskState.php
deleted file mode 100644
index ebd045bea3..0000000000
--- a/lib/classes/module/console/taskState.php
+++ /dev/null
@@ -1,117 +0,0 @@
- 13,
- \task_abstract::STATE_STARTED => 10,
- \task_abstract::STATE_TOSTART => 14,
- \task_abstract::STATE_TORESTART => 15,
- \task_abstract::STATE_STOPPED => 11,
- \task_abstract::STATE_TODELETE => 16
- );
-
- public function __construct($name = null)
- {
- parent::__construct($name);
-
- $this->addArgument('task_id', InputArgument::REQUIRED, 'The task_id to test');
-
- $this->setDescription('Returns a Phraseanet task state given its id');
-
- $this->addOption(
- 'short'
- , NULL
- , InputOption::VALUE_NONE
- , 'print short result, ie: stopped() | started(12345) | tostop(12345) | ...'
- , NULL
- );
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- if (!$this->container['phraseanet.configuration-tester']->isInstalled()) {
- return self::EXITCODE_SETUP_ERROR;
- }
-
- $task_id = (int) $input->getArgument('task_id');
- if ($task_id <= 0 || strlen($task_id) !== strlen($input->getArgument('task_id'))) {
- $output->writeln($input->getOption('short') ? 'bad_id' : 'Argument must be an ID');
-
- return self::EXITCODE_BAD_ARGUMENT;
- }
-
- $task_manager = $this->container['task-manager'];
-
- $taskPID = $taskState = NULL;
- $exitCode = 0;
-
- $task = NULL;
- try {
- $task = $task_manager->getTask($task_id);
- $taskPID = $task->getPID();
- $taskState = $task->getState();
- } catch (NotFoundHttpException $e) {
- $output->writeln($input->getOption('short') ? 'unknown_id' : $e->getMessage());
-
- return self::EXITCODE_TASK_UNKNOWN;
- } catch (Exception $e) {
- $output->writeln($input->getOption('short') ? 'fatal_error' : $e->getMessage());
-
- return self::EXITCODE_FATAL_ERROR;
- }
-
- if ($input->getOption('short')) {
- $output->writeln(sprintf('%s(%s)', $taskState, $taskPID));
- } else {
- if ($taskPID !== NULL) {
- $output->writeln(sprintf(
- 'Task %d is %s on pid %d'
- , $task_id
- , $taskState
- , $taskPID
- ));
- } else {
- $output->writeln(sprintf('Task %d is %s', $task_id, $taskState));
- }
- }
-
- if (array_key_exists($taskState, $this->stateToExitCode)) {
- $exitCode = $this->stateToExitCode[$taskState];
- } else {
- $exitCode = self::EXITCODE_STATE_UNKNOWN;
- }
-
- return $exitCode;
- }
-}
diff --git a/lib/classes/module/console/tasklist.php b/lib/classes/module/console/tasklist.php
deleted file mode 100644
index 972be70dfa..0000000000
--- a/lib/classes/module/console/tasklist.php
+++ /dev/null
@@ -1,66 +0,0 @@
-setDescription('Lists Phraseanet tasks');
-
- return $this;
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- if (!$this->container['phraseanet.configuration-tester']->isInstalled()) {
- return self::EXITCODE_SETUP_ERROR;
- }
-
- try {
- $task_manager = $this->container['task-manager'];
- $tasks = $task_manager->getTasks();
-
- if (count($tasks) === 0) {
- $output->writeln('No tasks on your install !');
- }
-
- foreach ($tasks as $task) {
- $this->printTask($task, $output);
- }
-
- return 0;
- } catch (\Exception $e) {
- return 1;
- }
- }
-
- protected function printTask(task_abstract $task, OutputInterface $output)
- {
- $message = $task->getID() . "\t" . ($task->getState() ) . "\t" . $task->getTitle();
- $output->writeln($message);
-
- return $this;
- }
-}
diff --git a/lib/classes/module/console/taskrun.php b/lib/classes/module/console/taskrun.php
deleted file mode 100644
index f743f0d1f8..0000000000
--- a/lib/classes/module/console/taskrun.php
+++ /dev/null
@@ -1,169 +0,0 @@
-task = NULL;
- $this->shedulerPID = NULL;
-
- $this
- ->addArgument('task_id', InputArgument::REQUIRED, 'The task_id to run')
- ->addOption(
- 'runner',
- 'r',
- InputOption::VALUE_REQUIRED,
- 'The name of the runner (manual, scheduler...)',
- task_abstract::RUNNER_MANUAL
- )
- ->addOption(
- 'ttyloglevel',
- 't',
- InputOption::VALUE_REQUIRED,
- 'threshold : (DEBUG|INFO|WARNING|ERROR|CRITICAL|ALERT)',
- ''
- )
- ->setDescription('Runs a Phraseanet task given its id');
-
- return $this;
- }
-
- public function sig_handler($signo)
- {
- if ($this->task) {
- $this->task->log(sprintf("signal %s received", $signo));
- $this->task->setRunning(false);
- }
- }
-
- protected function doExecute(InputInterface $input, OutputInterface $output)
- {
- if (!$this->container['phraseanet.configuration-tester']->isInstalled()) {
- return self::EXITCODE_SETUP_ERROR;
- }
-
- $task_id = (int) $input->getArgument('task_id');
- if ($task_id <= 0 || strlen($task_id) !== strlen($input->getArgument('task_id'))) {
- throw new \RuntimeException('Argument must be an Id.');
- }
-
- $task_manager = $this->container['task-manager'];
- $logger = $task_manager->getLogger();
-
- if ($input->getOption('runner') === task_abstract::RUNNER_MANUAL) {
- $schedStatus = $task_manager->getSchedulerState();
-
- if ($schedStatus && $schedStatus['status'] == task_abstract::STATE_STARTED && $schedStatus['pid']) {
- $this->shedulerPID = $schedStatus['pid'];
- }
- $runner = task_abstract::RUNNER_MANUAL;
- } else {
- $runner = task_abstract::RUNNER_SCHEDULER;
- $schedStatus = $task_manager->getSchedulerState();
- if ($schedStatus && $schedStatus['status'] == task_abstract::STATE_STARTED && $schedStatus['pid']) {
- $this->shedulerPID = $schedStatus['pid'];
- }
- }
-
- $logfile = __DIR__ . '/../../../../logs/task_' . $task_id . '.log';
- $this->container['task-manager.logger']->pushHandler(new RotatingFileHandler($logfile, 10));
- $this->task = $task_manager->getTask($task_id, $this->container['task-manager.logger']);
-
- $lib2v = array(
- 'DEBUG' => \task_abstract::LOG_DEBUG,
- 'INFO' => \task_abstract::LOG_INFO,
- 'WARNING' => \task_abstract::LOG_WARNING,
- 'ERROR' => \task_abstract::LOG_ERROR,
- 'CRITICAL' => \task_abstract::LOG_CRITICAL,
- 'ALERT' => \task_abstract::LOG_ALERT
- );
-
- $tmpTask = $task_manager->getTask($task_id, null);
- unset($tmpTask);
-
- if (($ttyloglevel = strtoupper($input->getOption('ttyloglevel'))) != '') {
- if (!array_key_exists($ttyloglevel, $lib2v)) {
- throw(new RuntimeException(sprintf(
- "Bad value '%s' for option loglevel\nuse DEBUG|INFO|WARNING|ERROR|CRITICAL|ALERT", $ttyloglevel))
- );
- }
- $handler = new StreamHandler("php://stdout", $lib2v[$ttyloglevel]);
- $logger->pushHandler($handler);
- }
-
- $this->task = $task_manager->getTask($task_id, $logger);
-
- register_tick_function(array($this, 'tick_handler'), true);
- declare(ticks = 1);
-
- if (function_exists('pcntl_signal')) {
- pcntl_signal(SIGTERM, array($this, 'sig_handler'));
- pcntl_signal(SIGINT, array($this, 'sig_handler'));
- }
-
- try {
- $this->task->run($runner);
- } catch (Exception $e) {
- $this->task->log(sprintf("taskrun : exception from 'run()', %s \n", $e->getMessage()));
-
- return($e->getCode());
- }
-
- if ($input->getOption('runner') === task_abstract::RUNNER_MANUAL) {
- $runner = task_abstract::RUNNER_MANUAL;
- }
- }
-
- public function tick_handler()
- {
- static $start;
-
- if ($start === null) {
- $start = time();
- }
-
- if (time() - $start > 0) {
- if ($this->shedulerPID) {
- if (function_exists('posix_kill') && !posix_kill($this->shedulerPID, 0)) {
- if (method_exists($this->task, 'signal')) {
- $this->task->signal('SIGNAL_SCHEDULER_DIED');
- } else {
- $this->task->setState(task_abstract::STATE_TOSTOP);
- }
- }
-
- $start = time();
- }
- }
- }
-}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStartTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStartTest.php
new file mode 100644
index 0000000000..ef9e66a76c
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStartTest.php
@@ -0,0 +1,24 @@
+getMockBuilder('Alchemy\Phrasea\TaskManager\TaskManagerStatus')
+ ->disableOriginalConstructor()
+ ->getMock();
+ self::$DI['cli']['task-manager.status']->expects($this->once())
+ ->method('start');
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $command = new SchedulerStart();
+ $command->setContainer(self::$DI['cli']);
+ $command->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStateTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStateTest.php
new file mode 100644
index 0000000000..323873e4c1
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStateTest.php
@@ -0,0 +1,18 @@
+getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $command = new SchedulerState();
+ $command->setContainer(self::$DI['cli']);
+ $command->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStopTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStopTest.php
new file mode 100644
index 0000000000..b57e3a4025
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/SchedulerStopTest.php
@@ -0,0 +1,24 @@
+getMockBuilder('Alchemy\Phrasea\TaskManager\TaskManagerStatus')
+ ->disableOriginalConstructor()
+ ->getMock();
+ self::$DI['cli']['task-manager.status']->expects($this->once())
+ ->method('stop');
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $command = new SchedulerStop();
+ $command->setContainer(self::$DI['cli']);
+ $command->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/TaskListTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskListTest.php
new file mode 100644
index 0000000000..3bf72fb37f
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskListTest.php
@@ -0,0 +1,25 @@
+insertTwoTasks();
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $command = new TaskList();
+ $command->setContainer(self::$DI['cli']);
+
+ $application = new \Symfony\Component\Console\Application();
+ $application->add($command);
+
+ $setupCommand = $application->find('task:list');
+ $setupCommand->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/TaskManagerCommandTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskManagerCommandTest.php
new file mode 100644
index 0000000000..09a1b47b61
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskManagerCommandTest.php
@@ -0,0 +1,27 @@
+getMockBuilder('Alchemy\TaskManager\TaskManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ self::$DI['cli']['task-manager']->expects($this->once())
+ ->method('addSubscriber')
+ ->with($this->isInstanceOf('Alchemy\TaskManager\Event\TaskManagerSubscriber\LockFileSubscriber'));
+ self::$DI['cli']['task-manager']->expects($this->once())
+ ->method('start');
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $command = new TaskManagerCommand();
+ $command->setContainer(self::$DI['cli']);
+ $command->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/TaskRunTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskRunTest.php
new file mode 100644
index 0000000000..03115dbd8a
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskRunTest.php
@@ -0,0 +1,47 @@
+setName('Task')
+ ->setJobId('Null');
+
+ self::$DI['cli']['EM']->persist($task);
+ self::$DI['cli']['EM']->flush();
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $input->expects($this->any())
+ ->method('getArgument')
+ ->with('task_id')
+ ->will($this->returnValue($task->getId()));
+
+ $command = new TaskRun();
+ $command->setContainer(self::$DI['cli']);
+
+ $job = $this->getMock('Alchemy\Phrasea\TaskManager\Job\JobInterface');
+
+ self::$DI['cli']['task-manager.job-factory'] = $this->getMockBuilder('Alchemy\Phrasea\TaskManager\Job\Factory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ self::$DI['cli']['task-manager.job-factory']->expects($this->once())
+ ->method('create')
+ ->will($this->returnValue($job));
+
+ $job->expects($this->once())
+ ->method('run');
+ $job->expects($this->exactly(2))
+ ->method('addSubscriber');
+
+ $command->execute($input, $output);
+ }
+}
diff --git a/tests/Alchemy/Tests/Phrasea/Command/Task/TaskStateTest.php b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskStateTest.php
new file mode 100644
index 0000000000..2305cd4ce1
--- /dev/null
+++ b/tests/Alchemy/Tests/Phrasea/Command/Task/TaskStateTest.php
@@ -0,0 +1,32 @@
+setName('Task')
+ ->setJobId('Null');
+
+ self::$DI['cli']['EM']->persist($task);
+ self::$DI['cli']['EM']->flush();
+
+ $input = $this->getMock('Symfony\Component\Console\Input\InputInterface');
+ $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface');
+
+ $input->expects($this->any())
+ ->method('getArgument')
+ ->with('task_id')
+ ->will($this->returnValue($task->getId()));
+
+ $command = new TaskState();
+ $command->setContainer(self::$DI['cli']);
+ $command->execute($input, $output);
+ }
+}