diff --git a/tests/ElasticApmTests/Util/AssertMessageStack.php b/tests/ElasticApmTests/Util/AssertMessageStack.php index 56883efa3..66a1ca56d 100644 --- a/tests/ElasticApmTests/Util/AssertMessageStack.php +++ b/tests/ElasticApmTests/Util/AssertMessageStack.php @@ -39,7 +39,7 @@ final class AssertMessageStack implements LoggableInterface { /** @var bool */ - private static $isEnabled = true; + private static $isEnabled = false; /** @var ?AssertMessageStack */ private static $singleton = null; diff --git a/tests/ElasticApmTests/Util/StackTraceFrameExpectations.php b/tests/ElasticApmTests/Util/StackTraceFrameExpectations.php index cf4b8f4b9..2f46b5182 100644 --- a/tests/ElasticApmTests/Util/StackTraceFrameExpectations.php +++ b/tests/ElasticApmTests/Util/StackTraceFrameExpectations.php @@ -38,6 +38,9 @@ final class StackTraceFrameExpectations extends ExpectationsBase /** @var Optional */ public $function; + /** @var bool */ + public $isFunctionRegex = false; + /** @var Optional */ public $lineno; @@ -101,9 +104,35 @@ public static function fromStandaloneFunction(string $fileName, int $lineNumber, return $result; } + private static function convertFunctionNameToRegPattern(string $text): string + { + $result = $text; + $result = str_replace('\\', '\\\\', $result); + $result = str_replace('{', '\\{', $result); + $result = str_replace('}', '\\}', $result); + $result = str_replace('(', '\\(', $result); + $result = str_replace(')', '\\)', $result); + return '/^' . $result . '$/'; + } + public static function fromClosure(string $fileName, int $lineNumber, ?string $namespace, string $class, bool $isStatic): self { - $result = self::fromClassMethodUnknownLocation($class, $isStatic, ($namespace === null ? '' : ($namespace . '\\')) . '{closure}'); + // Before PHP 8.4: ElasticApmTests\\TestsSharedCode\\SpanStackTraceTestSharedCode::ElasticApmTests\\TestsSharedCode\\{closure} + // PHP 8.4: ElasticApmTests\\TestsSharedCode\\SpanStackTraceTestSharedCode::{closure:ElasticApmTests\\TestsSharedCode\\SpanStackTraceTestSharedCode::allSpanCreatingApis():207} + if (PHP_VERSION_ID < 80400) { + $result = self::fromClassMethodUnknownLocation($class, $isStatic, ($namespace === null ? '' : ($namespace . '\\')) . '{closure}'); + } else { + $result = self::fromClassMethodUnknownLocation($class, $isStatic, '{closure:__CLASS__::__METHOD__():__LINE__}'); + $expectedFunc = $result->function->getValue(); + TestCaseBase::assertNotNull($expectedFunc); + $expectedFuncRegex = self::convertFunctionNameToRegPattern($expectedFunc); + $expectedFuncRegex = str_replace('__CLASS__', '[a-zA-Z0-9\\\\]+', $expectedFuncRegex); + $expectedFuncRegex = str_replace('__METHOD__', '[a-zA-Z0-9]+', $expectedFuncRegex); + $expectedFuncRegex = str_replace('__LINE__', '[0-9]+', $expectedFuncRegex); + $result->function->setValue($expectedFuncRegex); + $result->isFunctionRegex = true; + } + $result->filename->setValue($fileName); $result->lineno->setValue($lineNumber); return $result; @@ -143,7 +172,20 @@ public function assertMatches(StackTraceFrame $actual): void $dbgCtx->add(['this' => $this]); TestCaseBase::assertSameExpectedOptional($this->filename, $actual->filename); - TestCaseBase::assertSameExpectedOptional($this->function, $actual->function); + if ($this->isFunctionRegex) { + if ($this->function->isValueSet()) { + $expectedFuncRegex = $this->function->getValue(); + $actualFunc = $actual->function; + if ($expectedFuncRegex === null) { + TestCaseBase::assertNull($actualFunc); + } else { + TestCaseBase::assertNotNull($actualFunc); + TestCaseBase::assertMatchesRegularExpression($expectedFuncRegex, $actualFunc); + } + } + } else { + TestCaseBase::assertSameExpectedOptional($this->function, $actual->function); + } TestCaseBase::assertSameExpectedOptional($this->lineno, $actual->lineno); } } diff --git a/tests/ElasticApmTests/Util/TestCaseBase.php b/tests/ElasticApmTests/Util/TestCaseBase.php index 986b72c6c..e38302186 100644 --- a/tests/ElasticApmTests/Util/TestCaseBase.php +++ b/tests/ElasticApmTests/Util/TestCaseBase.php @@ -1133,6 +1133,23 @@ public static function assertContainsEx($needle, iterable $haystack, string $mes } } + /** + * @inheritDoc + */ + public static function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void + { + AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs()); + + try { + $pregMatchRetVal = preg_match($pattern, $string); + $dbgCtx->add(compact('pregMatchRetVal')); + Assert::assertTrue($pregMatchRetVal > 0, $message); + } catch (AssertionFailedError $ex) { + self::addMessageStackToException($ex); + throw $ex; + } + } + public static function assertDirectoryDoesNotExist(string $directory, string $message = ''): void { /**