php-src/sapi/fpm/tests/tester.inc

1180 lines
30 KiB
PHP

<?php
namespace FPM;
use Adoy\FastCGI\Client;
require_once 'fcgi.inc';
require_once 'logtool.inc';
require_once 'response.inc';
class Tester
{
/**
* Config directory for included files.
*/
const CONF_DIR = __DIR__ . '/conf.d';
/**
* File extension for access log.
*/
const FILE_EXT_LOG_ACC = 'acc.log';
/**
* File extension for error log.
*/
const FILE_EXT_LOG_ERR = 'err.log';
/**
* File extension for slow log.
*/
const FILE_EXT_LOG_SLOW = 'slow.log';
/**
* File extension for PID file.
*/
const FILE_EXT_PID = 'pid';
/**
* @var array
*/
static private $supportedFiles = [
self::FILE_EXT_LOG_ACC,
self::FILE_EXT_LOG_ERR,
self::FILE_EXT_LOG_SLOW,
self::FILE_EXT_PID,
'src.php',
'ini',
'skip.ini',
'*.sock',
];
/**
* @var array
*/
static private $filesToClean = ['.user.ini'];
/**
* @var bool
*/
private $debug;
/**
* @var array
*/
private $clients;
/**
* @var LogTool
*/
private $logTool;
/**
* Configuration template
*
* @var string
*/
private $configTemplate;
/**
* The PHP code to execute
*
* @var string
*/
private $code;
/**
* @var array
*/
private $options;
/**
* @var string
*/
private $fileName;
/**
* @var resource
*/
private $masterProcess;
/**
* @var resource
*/
private $outDesc;
/**
* @var array
*/
private $ports = [];
/**
* @var string
*/
private $error;
/**
* The last response for the request call
*
* @var Response
*/
private $response;
/**
* Clean all the created files up
*
* @param int $backTraceIndex
*/
static public function clean($backTraceIndex = 1)
{
$filePrefix = self::getCallerFileName($backTraceIndex);
if (substr($filePrefix, -6) === 'clean.') {
$filePrefix = substr($filePrefix, 0, -6);
}
$filesToClean = array_merge(
array_map(
function($fileExtension) use ($filePrefix) {
return $filePrefix . $fileExtension;
},
self::$supportedFiles
),
array_map(
function($fileExtension) {
return __DIR__ . '/' . $fileExtension;
},
self::$filesToClean
)
);
// clean all the root files
foreach ($filesToClean as $filePattern) {
foreach (glob($filePattern) as $filePath) {
unlink($filePath);
}
}
// clean config files
if (is_dir(self::CONF_DIR)) {
foreach(glob(self::CONF_DIR . '/*.conf') as $name) {
unlink($name);
}
rmdir(self::CONF_DIR);
}
}
/**
* @param int $backTraceIndex
* @return string
*/
static private function getCallerFileName($backTraceIndex = 1)
{
$backtrace = debug_backtrace();
if (isset($backtrace[$backTraceIndex]['file'])) {
$filePath = $backtrace[$backTraceIndex]['file'];
} else {
$filePath = __FILE__;
}
return substr($filePath, 0, -strlen(pathinfo($filePath, PATHINFO_EXTENSION)));
}
/**
* @return bool|string
*/
static public function findExecutable()
{
$phpPath = getenv("TEST_PHP_EXECUTABLE");
for ($i = 0; $i < 2; $i++) {
$slashPosition = strrpos($phpPath, "/");
if ($slashPosition) {
$phpPath = substr($phpPath, 0, $slashPosition);
} else {
break;
}
}
if ($phpPath && is_dir($phpPath)) {
if (file_exists($phpPath."/fpm/php-fpm") && is_executable($phpPath."/fpm/php-fpm")) {
/* gotcha */
return $phpPath."/fpm/php-fpm";
}
$phpSbinFpmi = $phpPath."/sbin/php-fpm";
if (file_exists($phpSbinFpmi) && is_executable($phpSbinFpmi)) {
return $phpSbinFpmi;
}
}
// try local php-fpm
$fpmPath = dirname(__DIR__) . '/php-fpm';
if (file_exists($fpmPath) && is_executable($fpmPath)) {
return $fpmPath;
}
return false;
}
/**
* Skip test if any of the supplied files does not exist.
*
* @param mixed $files
*/
static public function skipIfAnyFileDoesNotExist($files)
{
if (!is_array($files)) {
$files = array($files);
}
foreach ($files as $file) {
if (!file_exists($file)) {
die("skip File $file does not exist");
}
}
}
/**
* Skip test if config file is invalid.
*
* @param string $configTemplate
* @throws \Exception
*/
static public function skipIfConfigFails(string $configTemplate)
{
$tester = new self($configTemplate, '', [], self::getCallerFileName());
$testResult = $tester->testConfig();
if ($testResult !== null) {
self::clean(2);
die("skip $testResult");
}
}
/**
* Skip test if IPv6 is not supported.
*/
static public function skipIfIPv6IsNotSupported()
{
@stream_socket_client('tcp://[::1]:0', $errno);
if ($errno != 111) {
die('skip IPv6 is not supported.');
}
}
/**
* Skip if running on Travis.
*
* @param $message
*/
static public function skipIfTravis($message)
{
if (getenv("TRAVIS")) {
die('skip Travis: ' . $message);
}
}
/**
* Tester constructor.
*
* @param string|array $configTemplate
* @param string $code
* @param array $options
* @param string $fileName
*/
public function __construct(
$configTemplate,
string $code = '',
array $options = [],
$fileName = null
) {
$this->configTemplate = $configTemplate;
$this->code = $code;
$this->options = $options;
$this->fileName = $fileName ?: self::getCallerFileName();
$this->logTool = new LogTool();
$this->debug = (bool) getenv('TEST_FPM_DEBUG');
}
/**
* @param string $ini
*/
public function setUserIni(string $ini)
{
$iniFile = __DIR__ . '/.user.ini';
file_put_contents($iniFile, $ini);
}
/**
* Test configuration file.
*
* @return null|string
* @throws \Exception
*/
public function testConfig()
{
$configFile = $this->createConfig();
$cmd = self::findExecutable() . ' -t -y ' . $configFile . ' 2>&1';
exec($cmd, $output, $code);
if ($code) {
return preg_replace("/\[.+?\]/", "", $output[0]);
}
return null;
}
/**
* Start PHP-FPM master process
*
* @param array $extraArgs
* @return bool
* @throws \Exception
*/
public function start(array $extraArgs = [])
{
$configFile = $this->createConfig();
$desc = $this->outDesc ? [] : [1 => array('pipe', 'w'), 2 => array('redirect', 1)];
$cmd = [self::findExecutable(), '-F', '-O', '-y', $configFile];
if (getenv('TEST_FPM_RUN_AS_ROOT')) {
$cmd[] = '--allow-to-run-as-root';
}
$cmd = array_merge($cmd, $extraArgs);
$this->masterProcess = proc_open($cmd, $desc, $pipes);
register_shutdown_function(
function($masterProcess) use($configFile) {
@unlink($configFile);
if (is_resource($masterProcess)) {
@proc_terminate($masterProcess);
while (proc_get_status($masterProcess)['running']) {
usleep(10000);
}
}
},
$this->masterProcess
);
if (!$this->outDesc !== false) {
$this->outDesc = $pipes[1];
}
return true;
}
/**
* Run until needle is found in the log.
*
* @param string $needle
* @param int $max
* @return bool
* @throws \Exception
*/
public function runTill(string $needle, $max = 10)
{
$this->start();
$found = false;
for ($i = 0; $i < $max; $i++) {
$line = $this->getLogLine();
if (is_null($line)) {
break;
}
if (preg_match($needle, $line) === 1) {
$found = true;
break;
}
}
$this->close(true);
if (!$found) {
return $this->error("The search pattern not found");
}
return true;
}
/**
* Check if connection works.
*
* @param string $host
* @param null|string $successMessage
* @param null|string $errorMessage
* @param int $attempts
* @param int $delay
*/
public function checkConnection(
$host = '127.0.0.1',
$successMessage = null,
$errorMessage = 'Connection failed',
$attempts = 20,
$delay = 50000
) {
$i = 0;
do {
if ($i > 0 && $delay > 0) {
usleep($delay);
}
$fp = @fsockopen($host, $this->getPort());
} while ((++$i < $attempts) && !$fp);
if ($fp) {
$this->message($successMessage);
fclose($fp);
} else {
$this->message($errorMessage);
}
}
/**
* Execute request with parameters ordered for better checking.
*
* @param string $address
* @param string|null $successMessage
* @param string|null $errorMessage
* @param string $uri
* @param string $query
* @param array $headers
* @return Response
*/
public function checkRequest(
string $address,
string $successMessage = null,
string $errorMessage = null,
$uri = '/ping',
$query = '',
$headers = []
) {
return $this->request($query, $headers, $uri, $address, $successMessage, $errorMessage);
}
/**
* Execute and check ping request.
*
* @param string $address
* @param string $pingPath
* @param string $pingResponse
*/
public function ping(
string $address = '{{ADDR}}',
string $pingResponse = 'pong',
string $pingPath = '/ping'
) {
$response = $this->request('', [], $pingPath, $address);
$response->expectBody($pingResponse, 'text/plain');
}
/**
* Execute and check status request(s).
*
* @param array $expectedFields
* @param string|null $address
* @param string $statusPath
* @param mixed $formats
* @throws \Exception
*/
public function status(
array $expectedFields,
string $address = null,
string $statusPath = '/status',
$formats = ['plain', 'html', 'xml', 'json']
) {
if (!is_array($formats)) {
$formats = [$formats];
}
require_once "status.inc";
$status = new Status();
foreach ($formats as $format) {
$query = $format === 'plain' ? '' : $format;
$response = $this->request($query, [], $statusPath, $address);
$status->checkStatus($response, $expectedFields, $format);
}
}
/**
* Execute request.
*
* @param string $query
* @param array $headers
* @param string|null $uri
* @param string|null $address
* @param string|null $successMessage
* @param string|null $errorMessage
* @param bool $connKeepAlive
* @return Response
*/
public function request(
string $query = '',
array $headers = [],
string $uri = null,
string $address = null,
string $successMessage = null,
string $errorMessage = null,
bool $connKeepAlive = false
) {
if ($this->hasError()) {
return new Response(null, true);
}
if (is_null($uri)) {
$uri = $this->makeFile('src.php', $this->code);
}
$params = array_merge(
[
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'GET',
'SCRIPT_FILENAME' => $uri,
'SCRIPT_NAME' => $uri,
'QUERY_STRING' => $query,
'REQUEST_URI' => $uri . ($query ? '?'.$query : ""),
'DOCUMENT_URI' => $uri,
'SERVER_SOFTWARE' => 'php/fcgiclient',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '7777',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => php_uname('n'),
'SERVER_PROTOCOL' => 'HTTP/1.1',
'DOCUMENT_ROOT' => __DIR__,
'CONTENT_TYPE' => '',
'CONTENT_LENGTH' => 0
],
$headers
);
try {
$this->response = new Response(
$this->getClient($address, $connKeepAlive)->request_data($params, false)
);
$this->message($successMessage);
} catch (\Exception $exception) {
if ($errorMessage === null) {
$this->error("Request failed", $exception);
} else {
$this->message($errorMessage);
}
$this->response = new Response();
}
if ($this->debug) {
$this->response->debugOutput();
}
return $this->response;
}
/**
* Get client.
*
* @param string $address
* @param bool $keepAlive
* @return Client
*/
private function getClient(string $address = null, $keepAlive = false)
{
$address = $address ? $this->processTemplate($address) : $this->getAddr();
if ($address[0] === '/') { // uds
$host = 'unix://' . $address;
$port = -1;
} elseif ($address[0] === '[') { // ipv6
$addressParts = explode(']:', $address);
$host = $addressParts[0];
if (isset($addressParts[1])) {
$host .= ']';
$port = $addressParts[1];
} else {
$port = $this->getPort();
}
} else { // ipv4
$addressParts = explode(':', $address);
$host = $addressParts[0];
$port = $addressParts[1] ?? $this->getPort();
}
if (!$keepAlive) {
return new Client($host, $port);
}
if (!isset($this->clients[$host][$port])) {
$client = new Client($host, $port);
$client->setKeepAlive(true);
$this->clients[$host][$port] = $client;
}
return $this->clients[$host][$port];
}
/**
* Display logs
*
* @param int $number
* @param string $ignore
*/
public function displayLog(int $number = 1, string $ignore = 'systemd')
{
/* Read $number lines or until EOF */
while ($number > 0 || ($number < 0 && !feof($this->outDesc))) {
$a = fgets($this->outDesc);
if (empty($ignore) || !strpos($a, $ignore)) {
echo $a;
$number--;
}
}
}
/**
* Get a single log line
*
* @return null|string
*/
private function getLogLine()
{
$read = [$this->outDesc];
$write = null;
$except = null;
if (stream_select($read, $write, $except, 2 )) {
return fgets($this->outDesc);
} else {
return null;
}
}
/**
* Get log lines
*
* @param int $number
* @param bool $skipBlank
* @param string $ignore
* @return array
*/
public function getLogLines(int $number = 1, bool $skipBlank = false, string $ignore = 'systemd')
{
$lines = [];
/* Read $n lines or until EOF */
while ($number > 0 || ($number < 0 && !feof($this->outDesc))) {
$line = $this->getLogLine();
if (is_null($line)) {
break;
}
if ((empty($ignore) || !strpos($line, $ignore)) && (!$skipBlank || strlen(trim($line)) > 0)) {
$lines[] = $line;
$number--;
}
}
return $lines;
}
/**
* @return mixed|string
*/
public function getLastLogLine()
{
$lines = $this->getLogLines();
return $lines[0] ?? '';
}
/**
* Send signal to the supplied PID or the server PID.
*
* @param string $signal
* @param int|null $pid
* @return string
*/
public function signal($signal, int $pid = null)
{
if (is_null($pid)) {
$pid = $this->getPid();
}
return exec("kill -$signal $pid");
}
/**
* Terminate master process
*/
public function terminate()
{
proc_terminate($this->masterProcess);
}
/**
* Close all open descriptors and process resources
*
* @param bool $terminate
*/
public function close($terminate = false)
{
if ($terminate) {
$this->terminate();
}
fclose($this->outDesc);
proc_close($this->masterProcess);
}
/**
* Create a config file.
*
* @param string $extension
* @return string
* @throws \Exception
*/
private function createConfig($extension = 'ini')
{
if (is_array($this->configTemplate)) {
$configTemplates = $this->configTemplate;
if (!isset($configTemplates['main'])) {
throw new \Exception('The config template array has to have main config');
}
$mainTemplate = $configTemplates['main'];
unset($configTemplates['main']);
if (!is_dir(self::CONF_DIR)) {
mkdir(self::CONF_DIR);
}
foreach ($configTemplates as $name => $configTemplate) {
$this->makeFile(
'conf',
$this->processTemplate($configTemplate),
self::CONF_DIR,
$name
);
}
} else {
$mainTemplate = $this->configTemplate;
}
return $this->makeFile($extension, $this->processTemplate($mainTemplate));
}
/**
* Process template string.
*
* @param string $template
* @return string
*/
private function processTemplate(string $template)
{
$vars = [
'FILE:LOG:ACC' => ['getAbsoluteFile', self::FILE_EXT_LOG_ACC],
'FILE:LOG:ERR' => ['getAbsoluteFile', self::FILE_EXT_LOG_ERR],
'FILE:LOG:SLOW' => ['getAbsoluteFile', self::FILE_EXT_LOG_SLOW],
'FILE:PID' => ['getAbsoluteFile', self::FILE_EXT_PID],
'RFILE:LOG:ACC' => ['getRelativeFile', self::FILE_EXT_LOG_ACC],
'RFILE:LOG:ERR' => ['getRelativeFile', self::FILE_EXT_LOG_ERR],
'RFILE:LOG:SLOW' => ['getRelativeFile', self::FILE_EXT_LOG_SLOW],
'RFILE:PID' => ['getRelativeFile', self::FILE_EXT_PID],
'ADDR:IPv4' => ['getAddr', 'ipv4'],
'ADDR:IPv4:ANY' => ['getAddr', 'ipv4-any'],
'ADDR:IPv6' => ['getAddr', 'ipv6'],
'ADDR:IPv6:ANY' => ['getAddr', 'ipv6-any'],
'ADDR:UDS' => ['getAddr', 'uds'],
'PORT' => ['getPort', 'ip'],
'INCLUDE:CONF' => self::CONF_DIR . '/*.conf',
];
$aliases = [
'ADDR' => 'ADDR:IPv4',
'FILE:LOG' => 'FILE:LOG:ERR',
];
foreach ($aliases as $aliasName => $aliasValue) {
$vars[$aliasName] = $vars[$aliasValue];
}
return preg_replace_callback(
'/{{([a-zA-Z0-9:]+)(\[\w+\])?}}/',
function ($matches) use ($vars) {
$varName = $matches[1];
if (!isset($vars[$varName])) {
$this->error("Invalid config variable $varName");
return 'INVALID';
}
$pool = $matches[2] ?? 'default';
$varValue = $vars[$varName];
if (is_string($varValue)) {
return $varValue;
}
$functionName = array_shift($varValue);
$varValue[] = $pool;
return call_user_func_array([$this, $functionName], $varValue);
},
$template
);
}
/**
* @param string $type
* @param string $pool
* @return string
*/
public function getAddr(string $type = 'ipv4', $pool = 'default')
{
$port = $this->getPort($type, $pool, true);
if ($type === 'uds') {
return $this->getFile($port . '.sock');
}
return $this->getHost($type) . ':' . $port;
}
/**
* @param string $type
* @param string $pool
* @param bool $useAsId
* @return int
*/
public function getPort(string $type = 'ip', $pool = 'default', $useAsId = false)
{
if ($type === 'uds' && !$useAsId) {
return -1;
}
if (isset($this->ports['values'][$pool])) {
return $this->ports['values'][$pool];
}
$port = ($this->ports['last'] ?? 9000 + PHP_INT_SIZE - 1) + 1;
$this->ports['values'][$pool] = $this->ports['last'] = $port;
return $port;
}
/**
* @param string $type
* @return string
*/
public function getHost(string $type = 'ipv4')
{
switch ($type) {
case 'ipv6-any':
return '[::]';
case 'ipv6':
return '[::1]';
case 'ipv4-any':
return '0.0.0.0';
default:
return '127.0.0.1';
}
}
/**
* Get listen address.
*
* @param string|null $template
* @return string
*/
public function getListen($template = null)
{
return $template ? $this->processTemplate($template) : $this->getAddr();
}
/**
* Get PID.
*
* @return int
*/
public function getPid()
{
$pidFile = $this->getFile('pid');
if (!is_file($pidFile)) {
return (int) $this->error("PID file has not been created");
}
$pidContent = file_get_contents($pidFile);
if (!is_numeric($pidContent)) {
return (int) $this->error("PID content '$pidContent' is not integer");
}
return (int) $pidContent;
}
/**
* @param string $extension
* @param string|null $dir
* @param string|null $name
* @return string
*/
private function getFile(string $extension, $dir = null, $name = null)
{
$fileName = (is_null($name) ? $this->fileName : $name . '.') . $extension;
return is_null($dir) ? $fileName : $dir . '/' . $fileName;
}
/**
* @param string $extension
* @return string
*/
private function getAbsoluteFile(string $extension)
{
return $this->getFile($extension);
}
/**
* @param string $extension
* @return string
*/
private function getRelativeFile(string $extension)
{
$fileName = rtrim(basename($this->fileName), '.');
return $this->getFile($extension, null, $fileName);
}
/**
* @param string $extension
* @param string $prefix
* @return string
*/
private function getPrefixedFile(string $extension, string $prefix = null)
{
$fileName = rtrim($this->fileName, '.');
if (!is_null($prefix)) {
$fileName = $prefix . '/' . basename($fileName);
}
return $this->getFile($extension, null, $fileName);
}
/**
* @param string $extension
* @param string $content
* @param string|null $dir
* @param string|null $name
* @return string
*/
private function makeFile(string $extension, string $content = '', $dir = null, $name = null)
{
$filePath = $this->getFile($extension, $dir, $name);
file_put_contents($filePath, $content);
return $filePath;
}
/**
* @param string|null $msg
*/
private function message($msg)
{
if ($msg !== null) {
echo "$msg\n";
}
}
/**
* @param string $msg
* @param \Exception|null $exception
*/
private function error($msg, \Exception $exception = null)
{
$this->error = 'ERROR: ' . $msg;
if ($exception) {
$this->error .= '; EXCEPTION: ' . $exception->getMessage();
}
$this->error .= "\n";
echo $this->error;
}
/**
* @return bool
*/
private function hasError()
{
return !is_null($this->error) || !is_null($this->logTool->getError());
}
/**
* Expect file with a supplied extension to exist.
*
* @param string $extension
* @param string $prefix
* @return bool
*/
public function expectFile(string $extension, $prefix = null)
{
$filePath = $this->getPrefixedFile($extension, $prefix);
if (!file_exists($filePath)) {
return $this->error("The file $filePath does not exist");
}
return true;
}
/**
* Expect file with a supplied extension to not exist.
*
* @param string $extension
* @param string $prefix
* @return bool
*/
public function expectNoFile(string $extension, $prefix = null)
{
$filePath = $this->getPrefixedFile($extension, $prefix);
if (file_exists($filePath)) {
return $this->error("The file $filePath exists");
}
return true;
}
/**
* Expect message to be written to FastCGI error stream.
*
* @param string $message
* @param int $limit
* @param int $repeat
*/
public function expectFastCGIErrorMessage(
string $message,
int $limit = 1024,
int $repeat = 0
) {
$this->logTool->setExpectedMessage($message, $limit, $repeat);
$this->logTool->checkTruncatedMessage($this->response->getErrorData());
}
/**
* Expect starting lines to be logged.
*/
public function expectLogStartNotices()
{
$this->logTool->expectStartingLines($this->getLogLines(2));
}
/**
* Expect terminating lines to be logged.
*/
public function expectLogTerminatingNotices()
{
$this->logTool->expectTerminatorLines($this->getLogLines(-1));
}
/**
* Expect log message that can span multiple lines.
*
* @param string $message
* @param int $limit
* @param int $repeat
* @param bool $decorated
* @param bool $wrapped
*/
public function expectLogMessage(
string $message,
int $limit = 1024,
int $repeat = 0,
bool $decorated = true,
bool $wrapped = true
) {
$this->logTool->setExpectedMessage($message, $limit, $repeat);
if ($wrapped) {
$logLines = $this->getLogLines(-1, true);
$this->logTool->checkWrappedMessage($logLines, true, $decorated);
} else {
$logLines = $this->getLogLines(1, true);
$this->logTool->checkTruncatedMessage($logLines[0] ?? '');
}
if ($this->debug) {
$this->message("-------------- LOG LINES: -------------");
var_dump($logLines);
$this->message("---------------------------------------\n");
}
}
/**
* Expect a single log line.
*
* @param string $message
* @return bool
*/
public function expectLogLine(string $message)
{
$messageLen = strlen($message);
$limit = $messageLen > 1024 ? $messageLen + 16 : 1024;
$this->logTool->setExpectedMessage($message, $limit);
$logLines = $this->getLogLines(1, true);
if ($this->debug) {
$this->message("LOG LINE: " . ($logLines[0] ?? ''));
}
return $this->logTool->checkWrappedMessage($logLines, false);
}
/**
* Expect a log debug message.
*
* @param string $message
* @param string|null $pool
* @return bool
*/
public function expectLogDebug(string $message, $pool = null)
{
return $this->logTool->expectDebug($this->getLastLogLine(), $message, $pool);
}
/**
* Expect a log notice.
*
* @param string $message
* @param string|null $pool
* @return bool
*/
public function expectLogNotice(string $message, $pool = null)
{
return $this->logTool->expectNotice($this->getLastLogLine(), $message, $pool);
}
/**
* Expect a log warning.
*
* @param string $message
* @param string|null $pool
* @return bool
*/
public function expectLogWarning(string $message, $pool = null)
{
return $this->logTool->expectWarning($this->getLastLogLine(), $message, $pool);
}
/**
* Expect a log error.
*
* @param string $message
* @param string|null $pool
* @return bool
*/
public function expectLogError(string $message, $pool = null)
{
return $this->logTool->expectError($this->getLastLogLine(), $message, $pool);
}
/**
* Expect a log alert.
*
* @param string $message
* @param string|null $pool
* @return bool
*/
public function expectLogAlert(string $message, $pool = null)
{
return $this->logTool->expectAlert($this->getLastLogLine(), $message, $pool);
}
/**
* Expect no log lines to be logged.
*
* @return bool
*/
public function expectNoLogMessages()
{
$logLines = $this->getLogLines(-1, true);
if (!empty($logLines)) {
return $this->error(
"Expected no log lines but following lines logged:\n" . implode("\n", $logLines)
);
}
return true;
}
/**
* Print content of access log.
*/
public function printAccessLog()
{
$accessLog = $this->getFile('acc.log');
if (is_file($accessLog)) {
print file_get_contents($accessLog);
}
}
}