Dites, en PHP J'ai un tas de tests unitaires. Disons qu'ils nécessitent l'exécution d'un service.
Idéalement, je veux que mon script de bootstrap :
- démarrer ce service
- attendre que le service atteigne un état souhaité
- donner le contrôle au cadre de test unitaire de son choix pour exécuter les tests
- faire le ménage à la fin des tests, en mettant fin au service de manière élégante, le cas échéant
- mettre en place un moyen de capturer toutes les sorties du service en cours de route pour la journalisation et le débogage.
J'utilise actuellement proc_open()
pour initialiser mon service, capturer la sortie en utilisant le mécanisme de pipe, vérifier que le service arrive à l'état dont j'ai besoin en examinant la sortie.
Cependant, à ce stade, je suis bloqué - comment puis-je capturer le reste de la sortie (y compris STDERR) pour le reste de la durée du script, tout en permettant à mes tests unitaires de s'exécuter ?
Je peux penser à quelques solutions potentiellement longues, mais avant d'investir du temps dans leur recherche, j'aimerais savoir si quelqu'un d'autre a été confronté à ce problème et quelles solutions il a trouvées, le cas échéant, sans influencer la réponse.
Editar:
Voici une version réduite de la classe que j'initialise dans mon script bootstrap (avec new ServiceRunner
), à titre de référence :
<?php
namespace Tests;
class ServiceRunner
{
/**
* @var resource[]
*/
private $servicePipes;
/**
* @var resource
*/
private $serviceProc;
/**
* @var resource
*/
private $temp;
public function __construct()
{
// Open my log output buffer
$this->temp = fopen('php://temp', 'r+');
fputs(STDERR,"Launching Service.\n");
$this->serviceProc = proc_open('/path/to/service', [
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w"),
], $this->servicePipes);
// Set the streams to non-blocking, so stream_select() works
stream_set_blocking($this->servicePipes[1], false);
stream_set_blocking($this->servicePipes[2], false);
// Set up array of pipes to select on
$readables = [$this->servicePipes[1], $this->servicePipes[2]);
while(false !== ($streams = stream_select($read = $readables, $w = [], $e = [], 1))) {
// Iterate over pipes that can be read from
foreach($read as $stream) {
// Fetch a line of input, and append to my output buffer
if($line = stream_get_line($stream, 8192, "\n")) {
fputs($this->temp, $line."\n");
}
// Break out of both loops if the service has attained the desired state
if(strstr($line, 'The Service is Listening' ) !== false) {
break 2;
}
// If the service has closed one of its output pipes, remove them from those we're selecting on
if($line === false && feof($stream)) {
$readables = array_diff($readables, [$stream]);
}
}
}
/* SOLUTION REQUIRED SOLUTION REQUIRED SOLUTION REQUIRED SOLUTION REQUIRED */
/* Set up the pipes to be redirected to $this->temp here */
register_shutdown_function([$this, 'shutDown']);
}
public function shutDown()
{
fputs(STDERR,"Closing...\n");
fclose($this->servicePipes[0]);
proc_terminate($this->serviceProc, SIGINT);
fclose($this->servicePipes[1]);
fclose($this->servicePipes[2]);
proc_close($this->serviceProc);
fputs(STDERR,"Closed service\n");
$logFile = fopen('log.txt', 'w');
rewind($this->temp);
stream_copy_to_stream($this->temp, $logFile);
fclose($this->temp);
fclose($logFile);
}
}