当 运行 作为 Behat 上下文中的 Symfony Process 时,使用 Expect 生成交互式 CLI 命令会提前以 0 退出
Spawning interactive CLI command with Expect exits with 0 early, when run as Symfony Process in Behat context
这是一个相当高级的问题,也许不需要了解 Symfony 和 Behat 就可以理解这个问题。
因此,为了测试交互式 CLI 应用程序的输入和输出 bin/albumgrab
,我在 PHP 中使用 Symfony 控制台组件进行了编写,我将我的 Behat 功能上下文设置为通过 exec
.
构建 expect
脚本,运行
此 exec
命令是 运行 在 PHP 中通过 Symfony Process
。
$expect = <<<BASH
exec expect -c '
set timeout 180
spawn bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/?type=3&src=https%3A%2F%2Ffbcdn-sphotos-g-a.akamaihd.net%2Fhphotos-ak-xfp1%2Fv%2Ft1.0-9%2F10986697_10153559172718496_5727444485530442900_n.jpg%3Foh%3Dc47770f4cd15fecc6888bcd504899087%26oe%3D55DA9CB0%26__gda__%3D1439174101_7c78a93bf247dbad6c56681b6db5309c&size=960%2C959&fbid=10153559172718496\n"
interact
'
BASH;
$process = new Symfony\Component\Process\Process($expect);
$process->mustRun();
然而,当它通过第二个输入时,它似乎退出但成功。
通话中:
$process->setTty(true);
一直运行,但会直接打印到标准输出,我无法再捕获输出来做出断言,即使使用 PHP 的输出缓冲也是如此。
我认为 PTY 更合适:
$process->setPty(true);
因为它是 this Whosebug question 的解决方案。然而,这并不是到处都支持,至少在 Mac OS X 上不支持。
您可以在 Github 中看到我到目前为止所做的尝试:https://github.com/adamelso/albumgrab/pull/13/files and the Travis output for the last attempt https://travis-ci.org/adamelso/albumgrab/jobs/61137499
所以我的主要问题是为什么它总是以 0 提前退出以及如何防止它?
为了能够收到问题的答案 - 您肯定需要终端 (TTY) 或伪终端 (PTY) 来获取任何用户输入。
这就是为什么 - 没有 $process->setTty(true)
或 setPty(true)
- QuestionHelper 静默回退到默认值并且命令成功并退出代码 0。
现在 - 要使用示例用户输入测试您的命令 - 您应该使用 symfony 的控制台助手组件而不是使用 expect。
Symfony\Component\Console\Helper\HelperSet
Symfony\Component\Console\Tester\CommandTester
食谱章节 Testing a Command that expects input 中介绍了如何使用这些助手。
根据对 Expect Script wait Command Hangs 的回答,您需要等待 EOF 而不是使用 interact
命令:
set timeout 180
spawn ./bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/\n"
expect EOF
这是我用来在 OS X:
上测试的完整脚本
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\Process\Process;
$expect = <<<BASH
exec expect -c '
set timeout 180
spawn ./bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/?type=3&src=https%3A%2F%2Ffbcdn-sphotos-g-a.akamaihd.net%2Fhphotos-ak-xfp1%2Fv%2Ft1.0-9%2F10986697_10153559172718496_5727444485530442900_n.jpg%3Foh%3Dc47770f4cd15fecc6888bcd504899087%26oe%3D55DA9CB0%26__gda__%3D1439174101_7c78a93bf247dbad6c56681b6db5309c&size=960%2C959&fbid=10153559172718496\n"
expect EOF
'
BASH;
$process = new Process($expect);
$process->setPty(true);
$process->start();
$process->wait(function ($type, $buffer) {
if (Process::ERR === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
if (!$process->isSuccessful()) {
throw new \RuntimeException($process->getErrorOutput());
}
这是一个相当高级的问题,也许不需要了解 Symfony 和 Behat 就可以理解这个问题。
因此,为了测试交互式 CLI 应用程序的输入和输出 bin/albumgrab
,我在 PHP 中使用 Symfony 控制台组件进行了编写,我将我的 Behat 功能上下文设置为通过 exec
.
expect
脚本,运行
此 exec
命令是 运行 在 PHP 中通过 Symfony Process
。
$expect = <<<BASH
exec expect -c '
set timeout 180
spawn bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/?type=3&src=https%3A%2F%2Ffbcdn-sphotos-g-a.akamaihd.net%2Fhphotos-ak-xfp1%2Fv%2Ft1.0-9%2F10986697_10153559172718496_5727444485530442900_n.jpg%3Foh%3Dc47770f4cd15fecc6888bcd504899087%26oe%3D55DA9CB0%26__gda__%3D1439174101_7c78a93bf247dbad6c56681b6db5309c&size=960%2C959&fbid=10153559172718496\n"
interact
'
BASH;
$process = new Symfony\Component\Process\Process($expect);
$process->mustRun();
然而,当它通过第二个输入时,它似乎退出但成功。
通话中:
$process->setTty(true);
一直运行,但会直接打印到标准输出,我无法再捕获输出来做出断言,即使使用 PHP 的输出缓冲也是如此。
我认为 PTY 更合适:
$process->setPty(true);
因为它是 this Whosebug question 的解决方案。然而,这并不是到处都支持,至少在 Mac OS X 上不支持。
您可以在 Github 中看到我到目前为止所做的尝试:https://github.com/adamelso/albumgrab/pull/13/files and the Travis output for the last attempt https://travis-ci.org/adamelso/albumgrab/jobs/61137499
所以我的主要问题是为什么它总是以 0 提前退出以及如何防止它?
为了能够收到问题的答案 - 您肯定需要终端 (TTY) 或伪终端 (PTY) 来获取任何用户输入。
这就是为什么 - 没有 $process->setTty(true)
或 setPty(true)
- QuestionHelper 静默回退到默认值并且命令成功并退出代码 0。
现在 - 要使用示例用户输入测试您的命令 - 您应该使用 symfony 的控制台助手组件而不是使用 expect。
Symfony\Component\Console\Helper\HelperSet
Symfony\Component\Console\Tester\CommandTester
食谱章节 Testing a Command that expects input 中介绍了如何使用这些助手。
根据对 Expect Script wait Command Hangs 的回答,您需要等待 EOF 而不是使用 interact
命令:
set timeout 180
spawn ./bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/\n"
expect EOF
这是我用来在 OS X:
上测试的完整脚本<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\Process\Process;
$expect = <<<BASH
exec expect -c '
set timeout 180
spawn ./bin/albumgrab
expect "Please enter the name of the directory"
send "/tmp/php-london\n"
expect "Please enter the URL to the first image"
send "https://www.facebook.com/PeeHPLondon/photos/pb.7119218495.-2207520000.1430669248./10153559172718496/?type=3&src=https%3A%2F%2Ffbcdn-sphotos-g-a.akamaihd.net%2Fhphotos-ak-xfp1%2Fv%2Ft1.0-9%2F10986697_10153559172718496_5727444485530442900_n.jpg%3Foh%3Dc47770f4cd15fecc6888bcd504899087%26oe%3D55DA9CB0%26__gda__%3D1439174101_7c78a93bf247dbad6c56681b6db5309c&size=960%2C959&fbid=10153559172718496\n"
expect EOF
'
BASH;
$process = new Process($expect);
$process->setPty(true);
$process->start();
$process->wait(function ($type, $buffer) {
if (Process::ERR === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
if (!$process->isSuccessful()) {
throw new \RuntimeException($process->getErrorOutput());
}