Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions tests/Unit/WebServerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace DDTrace\Tests\Unit;

use DDTrace\Tests\Common\BaseTestCase;
use DDTrace\Tests\Sapi\Sapi;
use DDTrace\Tests\WebServer;

final class WebServerTest extends BaseTestCase
{
private $temporaryDirectory;

protected function ddTearDown()
{
if ($this->temporaryDirectory) {
@unlink($this->temporaryDirectory . '/index.php');
@unlink($this->temporaryDirectory . '/' . WebServer::ERROR_LOG_NAME);
@rmdir($this->temporaryDirectory);
}

parent::ddTearDown();
}

public function testCheckErrorsConsumesLogEntriesOnce()
{
$server = $this->createWebServer();
$errorLog = $this->temporaryDirectory . '/' . WebServer::ERROR_LOG_NAME;

file_put_contents($errorLog, "[ddtrace] [error] first error\n");

$this->assertSame('[ddtrace] [error] first error', $server->checkErrors());
$this->assertNull($server->checkErrors());

file_put_contents($errorLog, "[ddtrace] [warn] second error\n", FILE_APPEND);

$this->assertSame('[ddtrace] [warn] second error', $server->checkErrors());
$this->assertNull($server->checkErrors());
}

public function testCheckErrorsHandlesTruncatedLog()
{
$server = $this->createWebServer();
$errorLog = $this->temporaryDirectory . '/' . WebServer::ERROR_LOG_NAME;

file_put_contents($errorLog, "[ddtrace] [error] a deliberately long error\n");
$server->checkErrors();

file_put_contents($errorLog, "[ddtrace] [error] new\n");

$this->assertSame('[ddtrace] [error] new', $server->checkErrors());
$this->assertNull($server->checkErrors());
}

public function testCheckErrorsHandlesReplacedLogGrownPastPreviousOffset()
{
$server = $this->createWebServer();
$errorLog = $this->temporaryDirectory . '/' . WebServer::ERROR_LOG_NAME;

// Long enough that a naive size-only comparison would not detect the
// replacement once the new file grows to at least this offset.
$padding = str_repeat('x', 4096);
file_put_contents($errorLog, "[ddtrace] [error] first error $padding\n");
$server->checkErrors();

// Simulate rotation: create a new file elsewhere and rename it over the
// old path, guaranteeing a new inode, already containing more bytes than
// the previous offset.
$replacement = $errorLog . '.1';
file_put_contents($replacement, "[ddtrace] [error] early marker in replacement\n$padding\n");
rename($replacement, $errorLog);

$this->assertSame('[ddtrace] [error] early marker in replacement', $server->checkErrors());
$this->assertNull($server->checkErrors());
}

private function createWebServer()
{
$temporaryFile = tempnam(sys_get_temp_dir(), 'ddtrace-webserver-test-');
@unlink($temporaryFile);
mkdir($temporaryFile);
$this->temporaryDirectory = $temporaryFile;

$indexFile = $this->temporaryDirectory . '/index.php';
touch($indexFile);

$server = new WebServer($indexFile);
$sapi = new WebServerTestSapi();
$property = new \ReflectionProperty($server, 'sapi');
$property->setAccessible(true);
$property->setValue($server, $sapi);

return $server;
}
}

final class WebServerTestSapi implements Sapi
{
public function start()
{
}

public function stop()
{
}

public function isFastCgi()
{
return false;
}

public function checkErrors()
{
return null;
}
}
27 changes: 26 additions & 1 deletion tests/WebServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ final class WebServer
];

private $errorLogSize = 0;
private $errorLogInode = 0;

/**
* Persisted apache instance for the lifetime of the testsuite - we use reload instead of restart to apply changes.
Expand Down Expand Up @@ -184,6 +185,7 @@ public function start()
}

$this->errorLogSize = (int)@filesize($this->defaultInis['error_log']);
$this->errorLogInode = (int)@fileinode($this->defaultInis['error_log']);

if ($this->roadrunnerVersion) {
$this->sapi = new RoadrunnerServer(
Expand Down Expand Up @@ -358,7 +360,30 @@ public function mergeInis($inis)
*/
public function checkErrors()
{
$diff = @file_get_contents($this->defaultInis['error_log'], false, null, $this->errorLogSize);
$errorLog = $this->defaultInis['error_log'];

// Only inspect log entries written since the previous check. Without
// advancing the offset, one transient error is reported again by every
// retry and every subsequent test that shares this web server.
clearstatcache(true, $errorLog);
$currentSize = (int) @filesize($errorLog);
$currentInode = (int) @fileinode($errorLog);
if ($currentSize < $this->errorLogSize || $currentInode !== $this->errorLogInode) {
// The log may have been truncated, rotated, or replaced while the
// server was running. Comparing the inode detects a replacement that
// has already grown to at least the previous offset, which a size-only
// check would miss. Start reading from the beginning of the new file.
$this->errorLogSize = 0;
}
$this->errorLogInode = $currentInode;

$diff = @file_get_contents($errorLog, false, null, $this->errorLogSize);
if ($diff === false) {
$diff = "";
} else {
$this->errorLogSize += strlen($diff);
Comment thread
bwoebi marked this conversation as resolved.
}

$out = "";
foreach (explode("\n", $diff) as $line) {
// Ignore sidecar retry errors for known-invalid test hostnames — these are
Expand Down
Loading